From 9be23b73eac00f99a16540696de029ecd842beb5 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 14 Sep 2023 14:04:10 +0200 Subject: [PATCH 001/130] QmlDesigner: Align code preview properly Change-Id: I05599041b69edf7c8ecaeeff0144d1a4ed6117c3 Reviewed-by: Thomas Hartmann --- .../connectionseditor/ConnectionsDialogForm.qml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index f3c6fc00bcb..3bd7343e49c 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -183,12 +183,15 @@ Column { color: StudioTheme.Values.themeToolbarBackground Text { - width: parent.width - 8 // twice the editor button margins - anchors.centerIn: parent + id: code + anchors.fill: parent + anchors.margins: 4 text: backend.source color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.myFontSize wrapMode: Text.WordWrap + horizontalAlignment: code.lineCount === 1 ? Text.AlignHCenter : Text.AlignLeft + verticalAlignment: Text.AlignVCenter } HelperWidgets.AbstractButton { From 3a7f41b78cbe9a55b49cf13f30eb6889f044894c Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Thu, 14 Sep 2023 16:38:54 +0200 Subject: [PATCH 002/130] Prevent creation of nonexistent binding expressions Change-Id: Id6f9f35cd40667d694fcdf06d51c4642ae71afcd Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../connectioneditor/bindingmodel.cpp | 49 ++++- .../connectioneditor/bindingmodel.h | 5 +- .../connectioneditorutils.cpp | 181 +++++++++--------- .../connectioneditor/connectioneditorutils.h | 22 ++- 4 files changed, 151 insertions(+), 106 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index 0b38b9d35d9..94910d34d07 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -3,8 +3,8 @@ #include "bindingmodel.h" #include "bindingmodelitem.h" -#include "connectionview.h" #include "connectioneditorutils.h" +#include "connectionview.h" #include "modelfwd.h" #include @@ -16,6 +16,8 @@ #include +#include + namespace QmlDesigner { BindingModel::BindingModel(ConnectionView *parent) @@ -250,15 +252,15 @@ BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel *parent) , m_sourceNodeProperty() { connect(&m_sourceNode, &StudioQmlComboBoxBackend::activated, this, [this]() { - expressionChanged(); + sourceNodeChanged(); }); connect(&m_sourceNodeProperty, &StudioQmlComboBoxBackend::activated, this, [this]() { - expressionChanged(); + sourcePropertyNameChanged(); }); connect(&m_property, &StudioQmlComboBoxBackend::activated, this, [this]() { - propertyNameChanged(); + targetPropertyNameChanged(); }); } @@ -267,7 +269,7 @@ void BindingModelBackendDelegate::update(const BindingProperty &property, Abstra if (!property.isValid()) return; - auto addName = [](QStringList&& list, const QString& name) { + auto addName = [](QStringList &&list, const QString &name) { if (!list.contains(name)) list.prepend(name); return std::move(list); @@ -282,7 +284,8 @@ void BindingModelBackendDelegate::update(const BindingProperty &property, Abstra m_sourceNode.setModel(sourceNodes); m_sourceNode.setCurrentText(sourceNodeName); - auto sourceproperties = addName(availableSourceProperties(property, view), sourcePropertyName); + auto availableProperties = availableSourceProperties(sourceNodeName, property, view); + auto sourceproperties = addName(std::move(availableProperties), sourcePropertyName); m_sourceNodeProperty.setModel(sourceproperties); m_sourceNodeProperty.setCurrentText(sourcePropertyName); @@ -316,14 +319,40 @@ StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty() return &m_sourceNodeProperty; } -void BindingModelBackendDelegate::expressionChanged() const +void BindingModelBackendDelegate::sourceNodeChanged() { - auto commit = [this]() { + BindingModel *model = qobject_cast(parent()); + QTC_ASSERT(model, return); + + ConnectionView *view = model->connectionView(); + QTC_ASSERT(view, return); + + const QString sourceNode = m_sourceNode.currentText(); + const QString sourceProperty = m_sourceNodeProperty.currentText(); + + BindingProperty targetProperty = model->currentProperty(); + QStringList properties = availableSourceProperties(sourceNode, targetProperty, view); + + if (!properties.contains(sourceProperty)) { + QSignalBlocker blocker(this); + properties.prepend("---"); + m_sourceNodeProperty.setModel(properties); + m_sourceNodeProperty.setCurrentText({"---"}); + } + sourcePropertyNameChanged(); +} + +void BindingModelBackendDelegate::sourcePropertyNameChanged() const +{ + const QString sourceProperty = m_sourceNodeProperty.currentText(); + if (sourceProperty.isEmpty() || sourceProperty == "---") + return; + + auto commit = [this, sourceProperty]() { BindingModel *model = qobject_cast(parent()); QTC_ASSERT(model, return); const QString sourceNode = m_sourceNode.currentText(); - const QString sourceProperty = m_sourceNodeProperty.currentText(); QString expression; if (sourceProperty.isEmpty()) expression = sourceNode; @@ -337,7 +366,7 @@ void BindingModelBackendDelegate::expressionChanged() const callLater(commit); } -void BindingModelBackendDelegate::propertyNameChanged() const +void BindingModelBackendDelegate::targetPropertyNameChanged() const { auto commit = [this]() { BindingModel *model = qobject_cast(parent()); diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h index 4c3dd1210e5..4f5032afc24 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h @@ -86,8 +86,9 @@ public: private: QString targetNode() const; - void expressionChanged() const; - void propertyNameChanged() const; + void sourceNodeChanged(); + void sourcePropertyNameChanged() const; + void targetPropertyNameChanged() const; StudioQmlComboBoxBackend *property(); StudioQmlComboBoxBackend *sourceNode(); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp index d43ac648a6f..c8f16cb3506 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "connectioneditorutils.h" -#include #include #include #include @@ -58,61 +57,66 @@ PropertyName uniquePropertyName(const PropertyName &suggestion, const ModelNode return {}; } -NodeMetaInfo dynamicTypeMetaInfo(const AbstractProperty& property) +NodeMetaInfo dynamicTypeNameToNodeMetaInfo(const TypeName &typeName, Model *model) { // Note: Uses old mechanism to create the NodeMetaInfo and supports // only types we care about in the connection editor. // TODO: Support all possible AbstractProperty types and move to the // AbstractProperty class. - if (property.dynamicTypeName() == "bool") - return property.model()->boolMetaInfo(); - else if (property.dynamicTypeName() == "int") - return property.model()->metaInfo("QML.int"); - else if (property.dynamicTypeName() == "real") - return property.model()->metaInfo("QML.real"); - else if (property.dynamicTypeName() == "color") - return property.model()->metaInfo("QML.color"); - else if (property.dynamicTypeName() == "string") - return property.model()->metaInfo("QML.string"); - else if (property.dynamicTypeName() == "url") - return property.model()->metaInfo("QML.url"); - else if (property.dynamicTypeName() == "variant") - return property.model()->metaInfo("QML.variant"); + if (typeName == "bool") + return model->boolMetaInfo(); + else if (typeName == "int") + return model->metaInfo("QML.int"); + else if (typeName == "real") + return model->metaInfo("QML.real"); + else if (typeName == "color") + return model->metaInfo("QML.color"); + else if (typeName == "string") + return model->metaInfo("QML.string"); + else if (typeName == "url") + return model->metaInfo("QML.url"); + else if (typeName == "variant") + return model->metaInfo("QML.variant"); else - qWarning() << __FUNCTION__ << " type " << property.dynamicTypeName() << "not found"; - return { }; + qWarning() << __FUNCTION__ << " type " << typeName << "not found"; + return {}; } -bool metaInfoIsCompatibleUnsafe(const NodeMetaInfo &sourceType, const NodeMetaInfo &targetType) +NodeMetaInfo dynamicTypeMetaInfo(const AbstractProperty &property) { - if (sourceType.isVariant()) + return dynamicTypeNameToNodeMetaInfo(property.dynamicTypeName(), property.model()); +} + +bool metaInfoIsCompatibleUnsafe(const NodeMetaInfo &target, const NodeMetaInfo &source) +{ + if (target.isVariant()) return true; - if (sourceType.isBool() && targetType.isBool()) + if (target == source) return true; - if (sourceType == targetType) + if (target.isBool() && source.isBool()) return true; - if (sourceType.isNumber() && targetType.isNumber()) + if (target.isNumber() && source.isNumber()) return true; - if (sourceType.isString() && targetType.isString()) + if (target.isString() && source.isString()) return true; - if (sourceType.isUrl() && targetType.isUrl()) + if (target.isUrl() && source.isUrl()) return true; - if (sourceType.isColor() && targetType.isColor()) + if (target.isColor() && source.isColor()) return true; return false; } -bool metaInfoIsCompatible(const NodeMetaInfo &sourceType, const PropertyMetaInfo &metaInfo) +bool metaInfoIsCompatible(const NodeMetaInfo &targetType, const PropertyMetaInfo &sourceInfo) { - NodeMetaInfo targetType = metaInfo.propertyType(); - return metaInfoIsCompatibleUnsafe(sourceType, targetType); + NodeMetaInfo sourceType = sourceInfo.propertyType(); + return metaInfoIsCompatibleUnsafe(targetType, sourceType); } QVariant typeConvertVariant(const QVariant &variant, const QmlDesigner::TypeName &typeName) @@ -194,7 +198,7 @@ void convertBindingToVariantProperty(const BindingProperty &property, const QVar convertPropertyType(property, value); } -bool isBindingExpression(const QVariant& value) +bool isBindingExpression(const QVariant &value) { if (value.metaType().id() != QMetaType::QString) return false; @@ -250,11 +254,30 @@ QString defaultExpressionForType(const TypeName &type) return expression; } +std::pair splitExpression(const QString &expression) +{ + // ### Todo from original code (getExpressionStrings): + // We assume no expressions yet + const QStringList stringList = expression.split(QLatin1String(".")); + + QString sourceNode = stringList.constFirst(); + QString propertyName; + for (int i = 1; i < stringList.size(); ++i) { + propertyName += stringList.at(i); + if (i != stringList.size() - 1) + propertyName += QLatin1String("."); + } + if (propertyName.isEmpty()) + std::swap(sourceNode, propertyName); + + return {sourceNode, propertyName}; +} + QStringList singletonsFromView(AbstractView *view) { RewriterView *rv = view->rewriterView(); if (!rv) - return { }; + return {}; QStringList out; for (const QmlTypeData &data : rv->getQMLTypes()) { @@ -264,6 +287,29 @@ QStringList singletonsFromView(AbstractView *view) return out; } +std::vector propertiesFromSingleton(const QString &name, AbstractView *view) +{ + Model *model = view->model(); + QTC_ASSERT(model, return {}); + + if (NodeMetaInfo metaInfo = model->metaInfo(name.toUtf8()); metaInfo.isValid()) + return metaInfo.properties(); + + return {}; +} + +QList dynamicPropertiesFromNode(const ModelNode &node) +{ + auto isDynamic = [](const AbstractProperty &p) { return p.isDynamic(); }; + auto byName = [](const AbstractProperty &a, const AbstractProperty &b) { + return a.name() < b.name(); + }; + + QList dynamicProperties = Utils::filtered(node.properties(), isDynamic); + Utils::sort(dynamicProperties, byName); + return dynamicProperties; +} + QStringList availableSources(AbstractView *view) { QStringList sourceNodes; @@ -295,7 +341,7 @@ QStringList availableTargetProperties(const BindingProperty &bindingProperty) return writableProperties; } - return { }; + return {}; } ModelNode getNodeByIdOrParent(AbstractView *view, const QString &id, const ModelNode &targetNode) @@ -309,19 +355,17 @@ ModelNode getNodeByIdOrParent(AbstractView *view, const QString &id, const Model return {}; } -QStringList availableSourceProperties(const BindingProperty &bindingProperty, AbstractView *view) +QStringList availableSourceProperties(const QString &id, + const BindingProperty &targetProperty, + AbstractView *view) { - const QString expression = bindingProperty.expression(); - const QStringList stringlist = expression.split(QLatin1String(".")); + ModelNode modelNode = getNodeByIdOrParent(view, id, targetProperty.parentModelNode()); - const QString &id = stringlist.constFirst(); - ModelNode modelNode = getNodeByIdOrParent(view, id, bindingProperty.parentModelNode()); - - NodeMetaInfo type; - if (bindingProperty.isDynamic()) { - type = dynamicTypeMetaInfo(bindingProperty); - } else if (auto metaInfo = bindingProperty.parentModelNode().metaInfo(); metaInfo.isValid()) { - type = metaInfo.property(bindingProperty.name()).propertyType(); + NodeMetaInfo targetType; + if (targetProperty.isDynamic()) { + targetType = dynamicTypeMetaInfo(targetProperty); + } else if (auto metaInfo = targetProperty.parentModelNode().metaInfo(); metaInfo.isValid()) { + targetType = metaInfo.property(targetProperty.name()).propertyType(); } else qWarning() << __FUNCTION__ << " no meta info for target node"; @@ -329,23 +373,19 @@ QStringList availableSourceProperties(const BindingProperty &bindingProperty, Ab if (!modelNode.isValid()) { QStringList singletons = singletonsFromView(view); if (singletons.contains(id)) { - Model *model = view->model(); - QTC_ASSERT(model, return {}); - if (NodeMetaInfo metaInfo = model->metaInfo(id.toUtf8()); metaInfo.isValid()) { - for (const auto &property : metaInfo.properties()) { - if (metaInfoIsCompatible(type, property)) - possibleProperties.push_back(QString::fromUtf8(property.name())); - } + for (const auto &property : propertiesFromSingleton(id, view)) { + if (metaInfoIsCompatible(targetType, property)) + possibleProperties.push_back(QString::fromUtf8(property.name())); } return possibleProperties; } - qWarning() << __FUNCTION__ << " invalid model node"; + qWarning() << __FUNCTION__ << " invalid model node: " << id; return {}; - } + } - auto isCompatible = [type](const AbstractProperty& other) { + auto isCompatible = [targetType](const AbstractProperty &other) { auto otherType = dynamicTypeMetaInfo(other); - return metaInfoIsCompatibleUnsafe(type, otherType); + return metaInfoIsCompatibleUnsafe(targetType, otherType); }; for (const VariantProperty &variantProperty : modelNode.variantProperties()) { @@ -361,7 +401,7 @@ QStringList availableSourceProperties(const BindingProperty &bindingProperty, Ab NodeMetaInfo metaInfo = modelNode.metaInfo(); if (metaInfo.isValid()) { for (const auto &property : metaInfo.properties()) { - if (metaInfoIsCompatible(type, property) ) + if (metaInfoIsCompatible(targetType, property)) possibleProperties.push_back(QString::fromUtf8(property.name())); } } else { @@ -371,35 +411,4 @@ QStringList availableSourceProperties(const BindingProperty &bindingProperty, Ab return possibleProperties; } -QList dynamicPropertiesFromNode(const ModelNode &node) -{ - auto isDynamic = [](const AbstractProperty &p) { return p.isDynamic(); }; - auto byName = [](const AbstractProperty &a, const AbstractProperty &b) { - return a.name() < b.name(); - }; - - QList dynamicProperties = Utils::filtered(node.properties(), isDynamic); - Utils::sort(dynamicProperties, byName); - return dynamicProperties; -} - -std::pair splitExpression(const QString &expression) -{ - // ### Todo from original code (getExpressionStrings): - // We assume no expressions yet - const QStringList stringList = expression.split(QLatin1String(".")); - - QString sourceNode = stringList.constFirst(); - QString propertyName; - for (int i = 1; i < stringList.size(); ++i) { - propertyName += stringList.at(i); - if (i != stringList.size() - 1) - propertyName += QLatin1String("."); - } - if (propertyName.isEmpty()) - std::swap(sourceNode, propertyName); - - return {sourceNode, propertyName}; -} - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h index 1f05d654596..b81cc97bbae 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h @@ -24,21 +24,27 @@ void showErrorMessage(const QString &text); QString idOrTypeName(const ModelNode &modelNode); PropertyName uniquePropertyName(const PropertyName &suggestion, const ModelNode &modelNode); -NodeMetaInfo dynamicTypeMetaInfo(const AbstractProperty& property); +NodeMetaInfo dynamicTypeMetaInfo(const AbstractProperty &property); +NodeMetaInfo dynamicTypeNameToNodeMetaInfo(const TypeName &typeName, Model *model); + QVariant typeConvertVariant(const QVariant &variant, const QmlDesigner::TypeName &typeName); void convertVariantToBindingProperty(const VariantProperty &property, const QVariant &value); void convertBindingToVariantProperty(const BindingProperty &property, const QVariant &value); -bool isBindingExpression(const QVariant& value); +bool isBindingExpression(const QVariant &value); bool isDynamicVariantPropertyType(const TypeName &type); QVariant defaultValueForType(const TypeName &type); QString defaultExpressionForType(const TypeName &type); - -QStringList availableSources(AbstractView *view); -QStringList availableTargetProperties(const BindingProperty &bindingProperty); -QStringList availableSourceProperties(const BindingProperty &bindingProperty, AbstractView *view); -QList dynamicPropertiesFromNode(const ModelNode &node); - std::pair splitExpression(const QString &expression); +QStringList singletonsFromView(AbstractView *view); +std::vector propertiesFromSingleton(const QString &name, AbstractView *view); + +QList dynamicPropertiesFromNode(const ModelNode &node); +QStringList availableSources(AbstractView *view); +QStringList availableTargetProperties(const BindingProperty &bindingProperty); +QStringList availableSourceProperties(const QString &id, + const BindingProperty &targetProp, + AbstractView *view); + } // namespace QmlDesigner From 26ac6c26062066e6f97d9ab6d4784aef7bd1e8a0 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Thu, 7 Sep 2023 16:11:02 +0200 Subject: [PATCH 003/130] QmlDesigner: Integrate Editor into Connections Task-number: QDS-10530 Change-Id: I579c5e5d55b2136b96e32a448315dda8e720f2fb Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../ConnectionsDialogForm.qml | 43 ++++++++++++++++++- .../bindingeditor/abstracteditordialog.cpp | 2 +- .../components/bindingeditor/actioneditor.cpp | 12 ++++++ .../components/bindingeditor/actioneditor.h | 3 ++ .../bindingeditor/actioneditordialog.cpp | 31 +++++++++++++ .../bindingeditor/actioneditordialog.h | 3 ++ .../bindingeditor/bindingeditorwidget.cpp | 34 ++++++++++++--- .../bindingeditor/bindingeditorwidget.h | 3 ++ .../connectioneditor/connectionmodel.cpp | 8 ++++ .../connectioneditor/connectionmodel.h | 2 + 10 files changed, 134 insertions(+), 7 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 3bd7343e49c..10b1331fa68 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -204,7 +204,48 @@ Column { style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.edit_medium tooltip: qsTr("Add something.") - onClicked: console.log("OPEN EDITOR") + onClicked: { + expressionDialogLoader.show() + } + } + + Loader { + id: expressionDialogLoader + parent: editor + anchors.fill: parent + visible: false + active: visible + + function show() { + expressionDialogLoader.visible = true + } + + sourceComponent: Item { + id: bindingEditorParent + + Component.onCompleted: { + bindingEditor.showWidget() + bindingEditor.text = backend.source + bindingEditor.showControls(false) + bindingEditor.setMultilne(true) + bindingEditor.updateWindowName() + } + + ActionEditor { + id: bindingEditor + + onRejected: { + hideWidget() + expressionDialogLoader.visible = false + } + + onAccepted: { + backend.setNewSource(bindingEditor.text) + hideWidget() + expressionDialogLoader.visible = false + } + } + } } } } diff --git a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp index 58238ad1664..8ee7ef1b9ed 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp @@ -71,7 +71,7 @@ QString AbstractEditorDialog::editorValue() const void AbstractEditorDialog::setEditorValue(const QString &text) { if (m_editorWidget) - m_editorWidget->document()->setPlainText(text); + m_editorWidget->setEditorTextWithIndentation(text); } void AbstractEditorDialog::unregisterAutoCompletion() diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp index 30c99b0f27a..0260077da86 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp @@ -83,6 +83,18 @@ void ActionEditor::hideWidget() } } +void ActionEditor::showControls(bool show) +{ + if (m_dialog) + m_dialog->showControls(show); +} + +void QmlDesigner::ActionEditor::setMultilne(bool multiline) +{ + if (m_dialog) + m_dialog->setMultiline(multiline); +} + QString ActionEditor::connectionValue() const { if (!m_dialog) diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h index 0a45e054b09..29e5b1d99a6 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h @@ -31,6 +31,9 @@ public: Q_INVOKABLE void showWidget(int x, int y); Q_INVOKABLE void hideWidget(); + Q_INVOKABLE void showControls(bool show); + Q_INVOKABLE void setMultilne(bool multiline); + QString connectionValue() const; void setConnectionValue(const QString &text); diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.cpp index 8fddcb1ade8..b5bb15c30cf 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.cpp @@ -376,6 +376,37 @@ void ActionEditorDialog::updateComboBoxes([[maybe_unused]] int index, ComboBox t } } +void ActionEditorDialog::showControls(bool show) +{ + if (m_comboBoxType) + m_comboBoxType->setVisible(show); + + if (m_actionPlaceholder) + m_actionPlaceholder->setVisible(show); + if (m_assignmentPlaceholder) + m_assignmentPlaceholder->setVisible(show); + + if (m_actionTargetItem) + m_actionTargetItem->setVisible(show); + if (m_actionMethod) + m_actionMethod->setVisible(show); + + if (m_assignmentTargetItem) + m_assignmentTargetItem->setVisible(show); + if (m_assignmentTargetProperty) + m_assignmentTargetProperty->setVisible(show); + if (m_assignmentSourceItem) + m_assignmentSourceItem->setVisible(show); + if (m_assignmentSourceProperty) + m_assignmentSourceProperty->setVisible(show); +} + +void ActionEditorDialog::setMultiline(bool multiline) +{ + if (m_editorWidget) + m_editorWidget->m_isMultiline = multiline; +} + void ActionEditorDialog::setupUIComponents() { m_comboBoxType = new QComboBox(this); diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.h b/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.h index 06427271b38..342f0d21238 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.h +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.h @@ -96,6 +96,9 @@ public: void updateComboBoxes(int idx, ComboBox type); + void showControls(bool show); + void setMultiline(bool multiline); + private: void setupUIComponents(); diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp index 2b0b9f16e9d..da52d37a7e0 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -46,7 +47,7 @@ BindingEditorWidget::BindingEditorWidget() ? tr("Meta+Space") : tr("Ctrl+Space"))); - connect(m_completionAction, &QAction::triggered, [this]() { + connect(m_completionAction, &QAction::triggered, this, [this]() { invokeAssist(TextEditor::Completion); }); } @@ -68,8 +69,17 @@ void BindingEditorWidget::unregisterAutoCompletion() bool BindingEditorWidget::event(QEvent *event) { if (event->type() == QEvent::KeyPress) { - QKeyEvent *keyEvent = static_cast(event); - if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && !keyEvent->modifiers()) { + const QKeyEvent *keyEvent = static_cast(event); + const bool returnPressed = (keyEvent->key() == Qt::Key_Return) + || (keyEvent->key() == Qt::Key_Enter); + const Qt::KeyboardModifiers mods = keyEvent->modifiers(); + constexpr Qt::KeyboardModifier submitModifier = Qt::ControlModifier; + const bool submitModed = mods.testFlag(submitModifier); + + if (!m_isMultiline && (returnPressed && !mods)) { + emit returnKeyClicked(); + return true; + } else if (m_isMultiline && (returnPressed && submitModed)) { emit returnKeyClicked(); return true; } @@ -81,8 +91,22 @@ std::unique_ptr BindingEditorWidget::createAssistIn [[maybe_unused]] TextEditor::AssistKind assistKind, TextEditor::AssistReason assistReason) const { return std::make_unique( - textCursor(), Utils::FilePath(), - assistReason, qmljsdocument->semanticInfo()); + textCursor(), Utils::FilePath(), assistReason, qmljsdocument->semanticInfo()); +} + +void BindingEditorWidget::setEditorTextWithIndentation(const QString &text) +{ + auto *doc = document(); + doc->setPlainText(text); + + //we don't need to indent an empty text + //but is also needed for safer text.length()-1 below + if (text.isEmpty()) + return; + + auto modifier = std::make_unique( + doc, QTextCursor{doc}); + modifier->indent(0, text.length()-1); } BindingDocument::BindingDocument() diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.h b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.h index be1da5d4ef1..5433996110a 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.h +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.h @@ -32,6 +32,8 @@ public: std::unique_ptr createAssistInterface( TextEditor::AssistKind assistKind, TextEditor::AssistReason assistReason) const override; + void setEditorTextWithIndentation(const QString &text); + signals: void returnKeyClicked(); @@ -39,6 +41,7 @@ public: QmlJSEditor::QmlJSEditorDocument *qmljsdocument = nullptr; Core::IContext *m_context = nullptr; QAction *m_completionAction = nullptr; + bool m_isMultiline = false; }; class BindingDocument : public QmlJSEditor::QmlJSEditorDocument diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 15ab1341a38..60d541a940b 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -758,6 +758,14 @@ void ConnectionModelBackendDelegate::removeElse() setupHandlerAndStatements(); } +void ConnectionModelBackendDelegate::setNewSource(const QString &newSource) +{ + setSource(newSource); + commitNewSource(newSource); + setupHandlerAndStatements(); + setupCondition(); +} + int ConnectionModelBackendDelegate::currentRow() const { return m_currentRow; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index bef5b79637d..300fcb061a4 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -284,6 +284,8 @@ public: Q_INVOKABLE void addElse(); Q_INVOKABLE void removeElse(); + Q_INVOKABLE void setNewSource(const QString &newSource); + void setCurrentRow(int i); void update(); From 32f8fc43114d6f06ff7b1c105a5b9fc0b864ae2e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 14 Sep 2023 16:41:01 +0200 Subject: [PATCH 004/130] QmlDesigner: Show dot properties if combobox is used Change-Id: I3ea4539f178349551854843838948bf02740eb5b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../connectioneditor/propertytreemodel.cpp | 29 ++++++++++++------- .../connectioneditor/propertytreemodel.h | 3 ++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index ae0f8972c5d..7d4c55fb9d8 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -102,9 +102,12 @@ const std::vector priorityListSignals = {"clicked", "rotationChanged"}; const std::vector priorityListProperties - = {"opacity", "visible", "value", "x", "y", "width", "height", "rotation", - "color", "scale", "state", "enabled", "z", "text", "pressed", "containsMouse", - "checked", "hovered", "down", "clip", "parent", "from", "true", "focus"}; + = {"opacity", "visible", "value", "x", "y", + "width", "height", "rotation", "color", "scale", + "state", "enabled", "z", "text", "pressed", + "containsMouse", "checked", "hovered", "down", "clip", + "parent", "from", "radius", "smooth", "true", + "focus", "border.width", "border.color"}; const std::vector priorityListSlots = {"toggle", "increase", @@ -335,6 +338,11 @@ int PropertyTreeModel::columnCount(const QModelIndex &) const return 1; } +void PropertyTreeModel::setIncludeDotPropertiesOnFirstLevel(bool b) +{ + m_includeDotPropertiesOnFirstLevel = b; +} + void PropertyTreeModel::setPropertyType(PropertyTypes type) { if (m_type == type) @@ -498,7 +506,6 @@ const std::vector PropertyTreeModel::getDynamicProperties( return propertyType == "url"; case ColorType: return propertyType == "color"; - case BoolType: return propertyType == "bool"; default: @@ -513,18 +520,15 @@ const std::vector PropertyTreeModel::getDynamicProperties( const std::vector PropertyTreeModel::sortedAndFilteredPropertyNames( const NodeMetaInfo &metaInfo, bool recursive) const { + qDebug() << Q_FUNC_INFO << metaInfo.typeName() << recursive; auto filtered = Utils::filtered(metaInfo.properties(), [this, recursive](const PropertyMetaInfo &metaInfo) { // if (!metaInfo.isWritable()) - lhs/rhs const PropertyName name = metaInfo.name(); - if (name.contains(".")) - return false; - - if (name.startsWith("icon.")) - return false; - if (name.startsWith("transformOriginPoint.")) + if (!m_includeDotPropertiesOnFirstLevel + && name.contains(".")) return false; return filterProperty(name, metaInfo, recursive); @@ -721,6 +725,9 @@ bool PropertyTreeModel::filterProperty(const PropertyName &name, const NodeMetaInfo propertyType = metaInfo.propertyType(); + if (m_includeDotPropertiesOnFirstLevel && metaInfo.isPointer()) + return false; + //We want to keep sub items with matching properties if (!recursive && metaInfo.isPointer() && sortedAndFilteredPropertyNames(propertyType, true).size() > 0) @@ -842,6 +849,8 @@ PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *parent) : m connect(&m_idCombboBox, &StudioQmlComboBoxBackend::activated, this, [this]() { handleIdChanged(); }); + + m_model.setIncludeDotPropertiesOnFirstLevel(true); } void PropertyTreeModelDelegate::setPropertyType(PropertyTreeModel::PropertyTypes type) diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h index 67836cb2698..b93820ac7b9 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h @@ -65,6 +65,8 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; + void setIncludeDotPropertiesOnFirstLevel(bool b); + struct DataCacheItem { ModelNode modelNode; @@ -125,6 +127,7 @@ private: PropertyTypes m_type = AllTypes; QString m_filter; mutable QHash> m_sortedAndFilteredPropertyNamesSignalsSlots; + bool m_includeDotPropertiesOnFirstLevel = false; }; class PropertyListProxyModel : public QAbstractListModel From 6fbf77b027127f01adb4b8ca8ff03693467f2c67 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 14 Sep 2023 16:54:51 +0200 Subject: [PATCH 005/130] QmlDesigner: Fix open/close Connections Editor Improve the selection mechanism in all 3 Connections Editor tabs. When a new item is added automatically select it and open the popup. When the current item is removed, close the dialog. When the dialog is closed deselect the item. Change-Id: I918bbff6b290b38d496de8c2fa5f31d617f4d21c Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../connectionseditor/BindingsListView.qml | 24 +++++++++++++++-- .../connectionseditor/ConnectionsListView.qml | 24 ++++++++++++++++- .../qmldesigner/connectionseditor/Main.qml | 9 ++++--- .../connectionseditor/PropertiesListView.qml | 26 +++++++++++++++++-- .../connectioneditor/connectionmodel.cpp | 12 ++++++++- 5 files changed, 86 insertions(+), 9 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml index 07a6fa7c8db..61433bc3ef5 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml @@ -62,11 +62,26 @@ ListView { property int rowWidth: root.rowSpace / root.numColumns property int rowRest: root.rowSpace % root.numColumns + function addBinding() { + ConnectionsEditorEditorBackend.bindingModel.add() + if (root.currentItem) + dialog.popup(root.currentItem.delegateMouseArea) + } + + function resetIndex() { + root.model.currentIndex = -1 + root.currentIndex = -1 + } + data: [ BindingsDialog { id: dialog visible: false backend: root.model.delegate + + onClosing: function(event) { + root.resetIndex() + } } ] @@ -80,6 +95,8 @@ ListView { required property string source required property string sourceProperty + property alias delegateMouseArea: mouseArea + width: ListView.view.width height: root.style.squareControlSize.height color: mouseArea.containsMouse ? @@ -177,7 +194,11 @@ ListView { id: toolTipArea tooltip: qsTr("This is a test.") anchors.fill: parent - onClicked: root.model.remove(itemDelegate.index) + onClicked: { + if (itemDelegate.ListView.isCurrentItem) + dialog.close() + root.model.remove(itemDelegate.index) + } } } } @@ -185,6 +206,5 @@ ListView { highlight: Rectangle { color: root.style.interaction - width: 600 } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml index e48c0b48603..3d5e1edd5c0 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml @@ -63,11 +63,27 @@ ListView { property int rowWidth: root.rowSpace / root.numColumns property int rowRest: root.rowSpace % root.numColumns + function addConnection() { + ConnectionsEditorEditorBackend.connectionModel.add() + if (root.currentItem) + dialog.popup(root.currentItem.delegateMouseArea) + } + + function resetIndex() { + root.model.currentIndex = -1 + root.currentIndex = -1 + dialog.backend.currentRow = -1 + } + data: [ ConnectionsDialog { id: dialog visible: false backend: root.model.delegate + + onClosing: function(event) { + root.resetIndex() + } } ] @@ -80,6 +96,8 @@ ListView { required property string target required property string action + property alias delegateMouseArea: mouseArea + width: ListView.view.width height: root.style.squareControlSize.height color: mouseArea.containsMouse ? @@ -168,7 +186,11 @@ ListView { id: toolTipArea tooltip: qsTr("This is a test.") anchors.fill: parent - onClicked: root.model.remove(itemDelegate.index) + onClicked: { + if (itemDelegate.ListView.isCurrentItem) + dialog.close() + root.model.remove(itemDelegate.index) + } } } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/Main.qml b/share/qtcreator/qmldesigner/connectionseditor/Main.qml index 74efe6dddfd..6343e546c73 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Main.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Main.qml @@ -94,11 +94,11 @@ Rectangle { tooltip: qsTr("Add something.") onClicked: { if (connections.checked) - ConnectionsEditorEditorBackend.connectionModel.add() + connectionsListView.addConnection() else if (bindings.checked) - ConnectionsEditorEditorBackend.bindingModel.add() + bindingsListView.addBinding() else if (properties.checked) - ConnectionsEditorEditorBackend.dynamicPropertiesModel.add() + propertiesListView.addProperty() } } } @@ -106,6 +106,7 @@ Rectangle { } ConnectionsListView { + id: connectionsListView visible: connections.checked width: parent.width height: parent.height - toolbar.height - column.spacing @@ -114,6 +115,7 @@ Rectangle { } BindingsListView { + id: bindingsListView visible: bindings.checked width: parent.width height: parent.height - toolbar.height - column.spacing @@ -122,6 +124,7 @@ Rectangle { } PropertiesListView { + id: propertiesListView visible: properties.checked width: parent.width height: parent.height - toolbar.height - column.spacing diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index 2f4382020b3..c731e5ac819 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -62,11 +62,26 @@ ListView { property int rowWidth: root.rowSpace / root.numColumns property int rowRest: root.rowSpace % root.numColumns + function addProperty() { + ConnectionsEditorEditorBackend.dynamicPropertiesModel.add() + if (root.currentItem) + dialog.popup(root.currentItem.delegateMouseArea) + } + + function resetIndex() { + root.model.currentIndex = -1 + root.currentIndex = -1 + } + data: [ PropertiesDialog { id: dialog visible: false backend: root.model.delegate + + onClosing: function(event) { + root.resetIndex() + } } ] @@ -80,6 +95,8 @@ ListView { required property string type required property string value + property alias delegateMouseArea: mouseArea + width: ListView.view.width height: root.style.squareControlSize.height color: mouseArea.containsMouse ? @@ -89,7 +106,9 @@ ListView { MouseArea { id: mouseArea + anchors.fill: parent + hoverEnabled: true property int currentIndex: root.currentIndex @@ -179,7 +198,11 @@ ListView { id: toolTipArea tooltip: qsTr("This is a test.") anchors.fill: parent - onClicked: root.model.remove(itemDelegate.index) + onClicked: { + if (itemDelegate.ListView.isCurrentItem) + dialog.close() + root.model.remove(itemDelegate.index) + } } } @@ -188,6 +211,5 @@ ListView { highlight: Rectangle { color: root.style.interaction - width: 600 } } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 60d541a940b..9b2102bbc3a 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -363,7 +363,11 @@ void ConnectionModel::abstractPropertyChanged(const AbstractProperty &abstractPr void ConnectionModel::deleteConnectionByRow(int currentRow) { SignalHandlerProperty targetSignal = signalHandlerPropertyForRow(currentRow); - QTC_ASSERT(targetSignal.isValid(), return ); + SignalHandlerProperty selectedSignal = signalHandlerPropertyForRow(currentIndex()); + + const bool targetEqualsSelected = targetSignal == selectedSignal; + + QTC_ASSERT(targetSignal.isValid(), return); ModelNode node = targetSignal.parentModelNode(); QTC_ASSERT(node.isValid(), return ); @@ -374,6 +378,9 @@ void ConnectionModel::deleteConnectionByRow(int currentRow) } else { node.destroy(); } + + if (!targetEqualsSelected) + selectProperty(selectedSignal); } void ConnectionModel::removeRowFromTable(const SignalHandlerProperty &property) @@ -804,6 +811,9 @@ void ConnectionModelBackendDelegate::update() if (m_blockReflection) return; + if (m_currentRow == -1) + return; + m_propertyTreeModel.resetModel(); m_propertyListProxyModel.setRowAndInternalId(0, internalRootIndex); From ac6d875d129e8c7ba182fa27cc5a54302f5b6152 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 14 Sep 2023 17:08:56 +0300 Subject: [PATCH 006/130] QmlDesigner: Display informative string for 3D support in Qt5 projects Fixes: QDS-10661 Change-Id: I91ba32e478039711758e19c11d385af9fac4c99f Reviewed-by: Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke Reviewed-by: Mats Honkamaa --- .../ContentLibraryEffectsView.qml | 2 + .../ContentLibraryMaterialsView.qml | 2 + .../MaterialBrowser.qml | 4 +- .../EmptyMaterialEditorPane.qml | 4 +- .../EmptyTextureEditorPane.qml | 4 +- .../contentlibrary/contentlibraryview.cpp | 2 + .../contentlibrary/contentlibrarywidget.cpp | 14 ++++ .../contentlibrary/contentlibrarywidget.h | 6 ++ .../components/edit3d/edit3dwidget.cpp | 76 ++++++++++--------- .../components/edit3d/edit3dwidget.h | 2 + .../materialbrowser/materialbrowsermodel.cpp | 14 ++++ .../materialbrowser/materialbrowsermodel.h | 6 ++ .../materialbrowser/materialbrowserview.cpp | 2 + .../materialeditorcontextobject.cpp | 14 ++++ .../materialeditorcontextobject.h | 6 ++ .../materialeditor/materialeditorview.cpp | 2 + .../textureeditorcontextobject.cpp | 14 ++++ .../textureeditorcontextobject.h | 6 ++ .../textureeditor/textureeditorview.cpp | 2 + 19 files changed, 143 insertions(+), 39 deletions(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml index 1fe8f52c134..7cb9a4721d8 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml @@ -91,6 +91,8 @@ HelperWidgets.ScrollView { text: { if (!ContentLibraryBackend.effectsModel.bundleExists) qsTr("No effects available.") + else if (!ContentLibraryBackend.rootView.isQt6Project) + qsTr("Content Library effects are not supported in Qt5 projects.") else if (!ContentLibraryBackend.rootView.hasQuick3DImport) qsTr("To use Content Library, first add the QtQuick3D module in the Components view.") else if (!ContentLibraryBackend.effectsModel.hasRequiredQuick3DImport) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml index 975e65e266b..f9678dcad80 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml @@ -99,6 +99,8 @@ HelperWidgets.ScrollView { text: { if (!materialsModel.matBundleExists) qsTr("No materials available. Make sure you have internet connection.") + else if (!ContentLibraryBackend.rootView.isQt6Project) + qsTr("Content Library materials are not supported in Qt5 projects.") else if (!ContentLibraryBackend.rootView.hasQuick3DImport) qsTr("To use Content Library, first add the QtQuick3D module in the Components view.") else if (!materialsModel.hasRequiredQuick3DImport) diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml index 9272d2321e4..2d7e8fe8fef 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml @@ -592,7 +592,9 @@ Item { anchors.centerIn: parent text: { - if (!materialBrowserModel.hasQuick3DImport) + if (!materialBrowserModel.isQt6Project) + qsTr("Material Browser is not supported in Qt5 projects.") + else if (!materialBrowserModel.hasQuick3DImport) qsTr("To use Material Browser, first add the QtQuick3D module in the Components view.") else if (!materialBrowserModel.hasMaterialLibrary) qsTr("Material Browser is disabled inside a non-visual component.") diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml index 005b4dfff1d..39b0929a442 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml @@ -35,7 +35,9 @@ PropertyEditorPane { Text { text: { - if (!hasQuick3DImport) + if (!isQt6Project) + qsTr("Material Editor is not supported in Qt5 projects.") + else if (!hasQuick3DImport) qsTr("To use Material Editor, first add the QtQuick3D module in the Components view.") else if (!hasMaterialLibrary) qsTr("Material Editor is disabled inside a non-visual component.") diff --git a/share/qtcreator/qmldesigner/textureEditorQmlSource/EmptyTextureEditorPane.qml b/share/qtcreator/qmldesigner/textureEditorQmlSource/EmptyTextureEditorPane.qml index 72f1c464f92..244f3beb83d 100644 --- a/share/qtcreator/qmldesigner/textureEditorQmlSource/EmptyTextureEditorPane.qml +++ b/share/qtcreator/qmldesigner/textureEditorQmlSource/EmptyTextureEditorPane.qml @@ -32,7 +32,9 @@ PropertyEditorPane { Text { text: { - if (!hasQuick3DImport) + if (!isQt6Project) + qsTr("Texture Editor is not supported in Qt5 projects.") + else if (!hasQuick3DImport) qsTr("To use Texture Editor, first add the QtQuick3D module in the Components view.") else if (!hasMaterialLibrary) qsTr("Texture Editor is disabled inside a non-visual component.") diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index d22a3d91f3f..572572a4c23 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -11,6 +11,7 @@ #include "contentlibrarytexture.h" #include "contentlibrarytexturesmodel.h" #include "contentlibrarywidget.h" +#include "externaldependenciesinterface.h" #include "nodelistproperty.h" #include "qmldesignerconstants.h" #include "qmlobjectnode.h" @@ -223,6 +224,7 @@ void ContentLibraryView::modelAttached(Model *model) const bool hasLibrary = materialLibraryNode().isValid(); m_widget->setHasMaterialLibrary(hasLibrary); m_widget->setHasQuick3DImport(m_hasQuick3DImport); + m_widget->setIsQt6Project(externalDependencies().isQt6Project()); m_sceneId = model->active3DSceneId(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index cf968a33a89..8b7c4d7d344 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -681,6 +681,20 @@ void ContentLibraryWidget::setHasActive3DScene(bool b) emit hasActive3DSceneChanged(); } +bool ContentLibraryWidget::isQt6Project() const +{ + return m_isQt6Project; +} + +void ContentLibraryWidget::setIsQt6Project(bool b) +{ + if (m_isQt6Project == b) + return; + + m_isQt6Project = b; + emit isQt6ProjectChanged(); +} + void ContentLibraryWidget::reloadQmlSource() { const QString materialBrowserQmlPath = qmlSourcesPath() + "/ContentLibrary.qml"; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index 5e41f289141..ab71a3dc799 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -32,6 +32,7 @@ class ContentLibraryWidget : public QFrame Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport NOTIFY hasQuick3DImportChanged) Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary NOTIFY hasMaterialLibraryChanged) Q_PROPERTY(bool hasActive3DScene READ hasActive3DScene WRITE setHasActive3DScene NOTIFY hasActive3DSceneChanged) + Q_PROPERTY(bool isQt6Project READ isQt6Project NOTIFY isQt6ProjectChanged) // Needed for a workaround for a bug where after drag-n-dropping an item, the ScrollView scrolls to a random position Q_PROPERTY(bool isDragging MEMBER m_isDragging NOTIFY isDraggingChanged) @@ -53,6 +54,9 @@ public: bool hasActive3DScene() const; void setHasActive3DScene(bool b); + bool isQt6Project() const; + void setIsQt6Project(bool b); + Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText); void setMaterialsModel(QPointer newMaterialsModel); @@ -83,6 +87,7 @@ signals: void hasMaterialLibraryChanged(); void hasActive3DSceneChanged(); void isDraggingChanged(); + void isQt6ProjectChanged(); protected: bool eventFilter(QObject *obj, QEvent *event) override; @@ -121,6 +126,7 @@ private: bool m_hasActive3DScene = false; bool m_hasQuick3DImport = false; bool m_isDragging = false; + bool m_isQt6Project = false; QString m_baseUrl; QString m_texturesUrl; QString m_textureIconsUrl; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 8c3d54bb4f2..f330094b71c 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -9,6 +9,7 @@ #include "edit3dtoolbarmenu.h" #include "edit3dview.h" #include "edit3dviewconfig.h" +#include "externaldependenciesinterface.h" #include "materialutils.h" #include "metainfo.h" #include "modelnodeoperations.h" @@ -168,28 +169,8 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) createContextMenu(); - m_mcuLabel = new QLabel(this); - m_mcuLabel->setText(tr("MCU project does not support Qt Quick 3D.")); - m_mcuLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); - fillLayout->addWidget(m_mcuLabel.data()); - // Onboarding label contains instructions for new users how to get 3D content into the project m_onboardingLabel = new QLabel(this); - QString labelText = - tr("Your file does not import Qt Quick 3D.

" - "To create a 3D view, add the" - " QtQuick3D" - " module in the" - " Components" - " view or click" - " here" - ".

" - "To import 3D assets, select" - " +" - " in the" - " Assets" - " view."); - m_onboardingLabel->setText(labelText.arg(Utils::creatorTheme()->color(Utils::Theme::TextColorLink).name())); m_onboardingLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); connect(m_onboardingLabel, &QLabel::linkActivated, this, &Edit3DWidget::linkActivated); fillLayout->addWidget(m_onboardingLabel.data()); @@ -316,6 +297,42 @@ bool Edit3DWidget::isSceneLocked() const return false; } +void Edit3DWidget::showOnboardingLabel() +{ + QString text; + const DesignerMcuManager &mcuManager = DesignerMcuManager::instance(); + if (mcuManager.isMCUProject()) { + const QStringList mcuAllowedList = mcuManager.allowedImports(); + if (!mcuAllowedList.contains("QtQuick3d")) + text = tr("3D view is not supported in MCU projects."); + } + + if (text.isEmpty()) { + if (m_view->externalDependencies().isQt6Project()) { + QString labelText = + tr("Your file does not import Qt Quick 3D.

" + "To create a 3D view, add the" + " QtQuick3D" + " module in the" + " Components" + " view or click" + " here" + ".

" + "To import 3D assets, select" + " +" + " in the" + " Assets" + " view."); + text = labelText.arg(Utils::creatorTheme()->color(Utils::Theme::TextColorLink).name()); + } else { + text = tr("3D view is not supported in Qt5 projects."); + } + } + + m_onboardingLabel->setText(text); + m_onboardingLabel->setVisible(true); +} + // Called by the view to update the "create" sub-menu when the Quick3D entries are ready. void Edit3DWidget::updateCreateSubMenu(const QList &entriesList) { @@ -420,23 +437,10 @@ void Edit3DWidget::showCanvas(bool show) } m_canvas->setVisible(show); - if (show) { + if (show) m_onboardingLabel->setVisible(false); - m_mcuLabel->setVisible(false); - } else { - bool quick3dAllowed = true; - const DesignerMcuManager &mcuManager = DesignerMcuManager::instance(); - if (mcuManager.isMCUProject()) { - const QStringList mcuAllowedList = mcuManager.allowedImports(); - if (!mcuAllowedList.contains("QtQuick3d")) - quick3dAllowed = false; - } - - m_onboardingLabel->setVisible(quick3dAllowed); - m_mcuLabel->setVisible(!quick3dAllowed); - } - - + else + showOnboardingLabel(); } QMenu *Edit3DWidget::visibilityTogglesMenu() const diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index f764f068bf4..b9826ca07b3 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -66,6 +66,8 @@ private: bool isPasteAvailable() const; bool isSceneLocked() const; + void showOnboardingLabel(); + QPointer m_edit3DView; QPointer m_view; QPointer m_canvas; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp index 81ec4cbb3a5..d36e78512bb 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp @@ -187,6 +187,20 @@ void MaterialBrowserModel::setHasMaterialLibrary(bool b) emit hasMaterialLibraryChanged(); } +bool MaterialBrowserModel::isQt6Project() const +{ + return m_isQt6Project; +} + +void MaterialBrowserModel::setIsQt6Project(bool b) +{ + if (m_isQt6Project == b) + return; + + m_isQt6Project = b; + emit isQt6ProjectChanged(); +} + QString MaterialBrowserModel::copiedMaterialType() const { return m_copiedMaterialType; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h index 24c34394386..337dce05507 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h @@ -23,6 +23,7 @@ class MaterialBrowserModel : public QAbstractListModel Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged) Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged) Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary WRITE setHasMaterialLibrary NOTIFY hasMaterialLibraryChanged) + Q_PROPERTY(bool isQt6Project READ isQt6Project NOTIFY isQt6ProjectChanged) Q_PROPERTY(QString copiedMaterialType READ copiedMaterialType WRITE setCopiedMaterialType NOTIFY copiedMaterialTypeChanged) Q_PROPERTY(QStringList defaultMaterialSections MEMBER m_defaultMaterialSections NOTIFY materialSectionsChanged) Q_PROPERTY(QStringList principledMaterialSections MEMBER m_principledMaterialSections NOTIFY materialSectionsChanged) @@ -49,6 +50,9 @@ public: bool hasMaterialLibrary() const; void setHasMaterialLibrary(bool b); + bool isQt6Project() const; + void setIsQt6Project(bool b); + bool isEmpty() const { return m_isEmpty; } QString copiedMaterialType() const; @@ -105,6 +109,7 @@ signals: const QmlDesigner::ModelNode &material, const QList &props, bool all); + void isQt6ProjectChanged(); private: bool isValidIndex(int idx) const; @@ -126,6 +131,7 @@ private: bool m_hasModelSelection = false; bool m_hasMaterialLibrary = false; bool m_allPropsCopied = true; + bool m_isQt6Project = false; QString m_copiedMaterialType; QPointer m_view; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 5349fc26ce7..7c2ca0abbaf 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -6,6 +6,7 @@ #include "bindingproperty.h" #include "createtexture.h" #include "designmodecontext.h" +#include "externaldependenciesinterface.h" #include "materialbrowsermodel.h" #include "materialbrowsertexturesmodel.h" #include "materialbrowserwidget.h" @@ -230,6 +231,7 @@ void MaterialBrowserView::modelAttached(Model *model) m_widget->clearSearchFilter(); m_widget->materialBrowserModel()->setHasMaterialLibrary(false); m_hasQuick3DImport = model->hasImport("QtQuick3D"); + m_widget->materialBrowserModel()->setIsQt6Project(externalDependencies().isQt6Project()); // Project load is already very busy and may even trigger puppet reset, so let's wait a moment // before refreshing the model diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp index 552133ac887..5661436a8f3 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp @@ -331,6 +331,20 @@ void MaterialEditorContextObject::setHasMaterialLibrary(bool b) emit hasMaterialLibraryChanged(); } +bool MaterialEditorContextObject::isQt6Project() const +{ + return m_isQt6Project; +} + +void MaterialEditorContextObject::setIsQt6Project(bool b) +{ + if (m_isQt6Project == b) + return; + + m_isQt6Project = b; + emit isQt6ProjectChanged(); +} + bool MaterialEditorContextObject::hasModelSelection() const { return m_hasModelSelection; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h index 4bb91134d80..8772f3e1d2b 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h @@ -39,6 +39,7 @@ class MaterialEditorContextObject : public QObject Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged) Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged) Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary WRITE setHasMaterialLibrary NOTIFY hasMaterialLibraryChanged) + Q_PROPERTY(bool isQt6Project READ isQt6Project NOTIFY isQt6ProjectChanged) Q_PROPERTY(QQmlPropertyMap *backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged) @@ -96,6 +97,9 @@ public: bool hasMaterialLibrary() const; void setHasMaterialLibrary(bool b); + bool isQt6Project() const; + void setIsQt6Project(bool b); + bool hasModelSelection() const; void setHasModelSelection(bool b); @@ -134,6 +138,7 @@ signals: void hasQuick3DImportChanged(); void hasMaterialLibraryChanged(); void hasModelSelectionChanged(); + void isQt6ProjectChanged(); private: void updatePossibleTypeIndex(); @@ -163,6 +168,7 @@ private: bool m_hasQuick3DImport = false; bool m_hasMaterialLibrary = false; bool m_hasModelSelection = false; + bool m_isQt6Project = false; ModelNode m_selectedMaterial; }; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index ac3a92c5d44..487d300ef97 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -9,6 +9,7 @@ #include "designdocument.h" #include "designmodewidget.h" #include "dynamicpropertiesmodel.h" +#include "externaldependenciesinterface.h" #include "itemlibraryinfo.h" #include "materialeditorqmlbackend.h" #include "materialeditorcontextobject.h" @@ -598,6 +599,7 @@ void MaterialEditorView::setupQmlBackend() currentQmlBackend->contextObject()->setHasMaterialLibrary(materialLibraryNode().isValid()); currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); currentQmlBackend->contextObject()->setCurrentType(currentTypeName); + currentQmlBackend->contextObject()->setIsQt6Project(externalDependencies().isQt6Project()); m_qmlBackEnd = currentQmlBackend; diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp index 8d1b03bddd6..73e784846bb 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp @@ -159,6 +159,20 @@ void TextureEditorContextObject::setHasMaterialLibrary(bool b) emit hasMaterialLibraryChanged(); } +bool TextureEditorContextObject::isQt6Project() const +{ + return m_isQt6Project; +} + +void TextureEditorContextObject::setIsQt6Project(bool b) +{ + if (m_isQt6Project == b) + return; + + m_isQt6Project = b; + emit isQt6ProjectChanged(); +} + bool TextureEditorContextObject::hasSingleModelSelection() const { return m_hasSingleModelSelection; diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.h b/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.h index 595f05e7c91..67cc23e063a 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.h +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.h @@ -39,6 +39,7 @@ class TextureEditorContextObject : public QObject Q_PROPERTY(bool hasSingleModelSelection READ hasSingleModelSelection WRITE setHasSingleModelSelection NOTIFY hasSingleModelSelectionChanged) Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary WRITE setHasMaterialLibrary NOTIFY hasMaterialLibraryChanged) + Q_PROPERTY(bool isQt6Project READ isQt6Project NOTIFY isQt6ProjectChanged) Q_PROPERTY(QQmlPropertyMap *backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged) @@ -95,6 +96,9 @@ public: bool hasMaterialLibrary() const; void setHasMaterialLibrary(bool b); + bool isQt6Project() const; + void setIsQt6Project(bool b); + bool hasSingleModelSelection() const; void setHasSingleModelSelection(bool b); @@ -133,6 +137,7 @@ signals: void hasMaterialLibraryChanged(); void hasSingleModelSelectionChanged(); void activeDragSuffixChanged(); + void isQt6ProjectChanged(); private: QUrl m_specificsUrl; @@ -157,6 +162,7 @@ private: bool m_hasQuick3DImport = false; bool m_hasMaterialLibrary = false; bool m_hasSingleModelSelection = false; + bool m_isQt6Project = false; ModelNode m_selectedTexture; diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 42d909400c3..d088dd7a26a 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -461,6 +462,7 @@ void TextureEditorView::setupQmlBackend() currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); bool hasValidSelection = QmlObjectNode(m_selectedModel).hasBindingProperty("materials"); currentQmlBackend->contextObject()->setHasSingleModelSelection(hasValidSelection); + currentQmlBackend->contextObject()->setIsQt6Project(externalDependencies().isQt6Project()); m_qmlBackEnd = currentQmlBackend; From fc981ee54af32c4e20ada2fdeed44d6c8104968e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 15 Sep 2023 12:30:10 +0200 Subject: [PATCH 007/130] QmlDesigner: Fix crash Check for existing device. Task-number: QDS-10137 Change-Id: I00065b63f09fb5691c52acaefd75fee0c4315d75 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index 0c027a99d5b..b2f94d4df4b 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -621,7 +622,10 @@ QStringList ToolBarBackend::kits() const { auto kits = Utils::filtered(ProjectExplorer::KitManager::kits(), [](ProjectExplorer::Kit *kit) { const auto qtVersion = QtSupport::QtKitAspect::qtVersion(kit); + const auto dev = ProjectExplorer::DeviceKitAspect::device(kit); + return kit->isValid() && !kit->isReplacementKit() && qtVersion && qtVersion->isValid() + && dev /*&& kit->isAutoDetected() */; }); From 5ed667c554dae83117f4dd8cbf84803534cf1771 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 15 Sep 2023 09:55:47 +0200 Subject: [PATCH 008/130] QmlDesigner: Add chevron to items with children Add chevron to the ItemDelegates of the SuggestionPopup if items have children. This indicates that the user can click on it and go deeper. Change-Id: I905769b8190b81a92025861a3fc38817de5b56ed Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../connectionseditor/MyListViewDelegate.qml | 49 ++++++++++++++----- .../connectionseditor/MyTreeViewDelegate.qml | 11 +++-- .../connectionseditor/SuggestionPopup.qml | 2 +- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/MyListViewDelegate.qml b/share/qtcreator/qmldesigner/connectionseditor/MyListViewDelegate.qml index 936d06daee6..5ca53985d85 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/MyListViewDelegate.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/MyListViewDelegate.qml @@ -3,27 +3,52 @@ import QtQuick import QtQuick.Controls +import QtQuick.Layouts +import StudioTheme as StudioTheme ItemDelegate { id: control - hoverEnabled: true - contentItem: Text { - leftPadding: 8 - rightPadding: 8 - text: control.text - font: control.font - opacity: enabled ? 1.0 : 0.3 - color: control.hovered ? "#111111" : "white" - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + hoverEnabled: true + verticalPadding: 0 + rightPadding: 10 // ScrollBar thickness + + contentItem: RowLayout { + Text { + Layout.fillWidth: true + leftPadding: 8 + rightPadding: 8 + text: control.text + font: control.font + opacity: enabled ? 1.0 : 0.3 + color: control.hovered ? control.style.text.selectedText : control.style.text.idle + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + Item { + visible: control.childCount + width: 30 + height: 30 + + Text { + id: chevronLeft + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: control.style.baseIconFontSize + color: control.hovered ? control.style.text.selectedText : control.style.text.idle + text: StudioTheme.Constants.forward_medium + anchors.centerIn: parent + } + } } background: Rectangle { implicitWidth: 200 implicitHeight: 30 opacity: enabled ? 1 : 0.3 - color: control.hovered ? "#4DBFFF" : "transparent" + color: control.hovered ? control.style.interaction : "transparent" } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/MyTreeViewDelegate.qml b/share/qtcreator/qmldesigner/connectionseditor/MyTreeViewDelegate.qml index 366860b6757..549cb0fd7b5 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/MyTreeViewDelegate.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/MyTreeViewDelegate.qml @@ -8,6 +8,9 @@ import StudioTheme as StudioTheme T.TreeViewDelegate { id: control + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + hoverEnabled: true implicitWidth: 200 implicitHeight: 30 @@ -28,8 +31,8 @@ T.TreeViewDelegate { Text { id: icon font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.smallIconFontSize - color: control.hovered ? "#111111" : "white" // TODO colors + font.pixelSize: control.style.smallIconFontSize + color: control.hovered ? control.style.text.selectedText : control.style.text.idle text: StudioTheme.Constants.sectionToggle rotation: control.expanded ? 0 : -90 anchors.centerIn: parent @@ -39,14 +42,14 @@ T.TreeViewDelegate { background: Rectangle { implicitWidth: 200 implicitHeight: 30 - color: control.hovered ? "#4DBFFF" : "transparent" + color: control.hovered ? control.style.interaction : "transparent" } contentItem: Text { text: control.text font: control.font opacity: enabled ? 1.0 : 0.3 - color: control.hovered ? "#111111" : "white" + color: control.hovered ? control.style.text.selectedText : control.style.text.idle horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter elide: Text.ElideRight diff --git a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml index fb9643ca14b..735bc9364cd 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml @@ -80,7 +80,7 @@ Controls.Popup { onClicked: { stack.pop(Controls.StackView.Immediate) - root.listModel.goUp() //treeModel.pop() + root.listModel.goUp() } } From 6d5ceadccf0a8d4bd0bf03dcd7e1fd6229134183 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 15 Sep 2023 10:40:18 +0200 Subject: [PATCH 009/130] QmlDesigner: Improve type selection * Disable custom item in Connection Editor type selection * Rename Unknown to Custom Change-Id: I9fa8c9ab6284503d5ccc61813454b9d10f291a06 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../ConnectionsDialogForm.qml | 40 +++++++++++++++---- .../StudioControls/TopLevelComboBox.qml | 13 ++++-- .../connectioneditorevaluator.cpp | 4 +- .../connectioneditorstatements.h | 4 +- .../connectioneditor/tst_connectioneditor.cpp | 10 ++--- 5 files changed, 51 insertions(+), 20 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 10b1331fa68..56998fac9e0 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -57,14 +57,38 @@ Column { onIndexFromBackendChanged: action.currentIndex = action.indexFromBackend onActivated: backend.changeActionType(action.currentValue) - model: [ - { value: ConnectionModelStatementDelegate.CallFunction, text: qsTr("Call Function") }, - { value: ConnectionModelStatementDelegate.Assign, text: qsTr("Assign") }, - { value: ConnectionModelStatementDelegate.ChangeState, text: qsTr("Change State") }, - { value: ConnectionModelStatementDelegate.SetProperty, text: qsTr("Set Property") }, - { value: ConnectionModelStatementDelegate.PrintMessage, text: qsTr("Print Message") }, - { value: ConnectionModelStatementDelegate.Custom, text: qsTr("Unknown") } - ] + model: ListModel { + ListElement { + value: ConnectionModelStatementDelegate.CallFunction + text: qsTr("Call Function") + enabled: true + } + ListElement { + value: ConnectionModelStatementDelegate.Assign + text: qsTr("Assign") + enabled: true + } + ListElement { + value: ConnectionModelStatementDelegate.ChangeState + text: qsTr("Change State") + enabled: true + } + ListElement { + value: ConnectionModelStatementDelegate.SetProperty + text: qsTr("Set Property") + enabled: true + } + ListElement { + vvalue: ConnectionModelStatementDelegate.PrintMessage + text: qsTr("Print Message") + enabled: true + } + ListElement { + value: ConnectionModelStatementDelegate.Custom + text: qsTr("Custom") + enabled: false + } + } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml index 61483feadfb..eb1a6c631d2 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml @@ -155,6 +155,7 @@ T.ComboBox { width: control.width height: control.style.controlSize.height padding: 0 + enabled: model.enabled === undefined ? true : model.enabled contentItem: Text { leftPadding: itemDelegateIconArea.width @@ -182,8 +183,13 @@ T.ComboBox { T.Label { id: itemDelegateIcon text: StudioTheme.Constants.tickIcon - color: itemDelegate.hovered ? control.style.text.selectedText - : control.style.text.idle + color: { + if (!itemDelegate.enabled) + return control.style.text.disabled + + return itemDelegate.hovered ? control.style.text.selectedText + : control.style.text.idle + } font.family: StudioTheme.Constants.iconFont.family font.pixelSize: control.style.smallIconFontSize visible: control.currentIndex === index @@ -200,7 +206,8 @@ T.ComboBox { y: 0 width: itemDelegate.width - 2 * control.style.borderWidth height: itemDelegate.height - color: itemDelegate.hovered ? control.style.interaction : "transparent" + color: itemDelegate.hovered && itemDelegate.enabled ? control.style.interaction + : "transparent" } } } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp index d44077e4ff6..c7d3ae977a5 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp @@ -760,14 +760,14 @@ QString ConnectionEditorEvaluator::getDisplayStringForType(const QString &statem newDoc->parseJavaScript(); if (!newDoc->isParsedCorrectly()) - return ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME; + return ConnectionEditorStatements::CUSTOM_DISPLAY_NAME; newDoc->ast()->accept(&evaluator); const bool valid = evaluator.status() == ConnectionEditorEvaluator::Succeeded; if (!valid) - return ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME; + return ConnectionEditorStatements::CUSTOM_DISPLAY_NAME; auto result = evaluator.resultNode(); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h index 63d457a84a4..06e6434b5ab 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h @@ -21,8 +21,8 @@ inline constexpr char LOG_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( "QmlDesigner::ConnectionEditorStatements", "Print"); inline constexpr char EMPTY_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( "QmlDesigner::ConnectionEditorStatements", "Empty"); -inline constexpr char UNKNOWN_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( - "QmlDesigner::ConnectionEditorStatements", "Unknown"); +inline constexpr char CUSTOM_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( + "QmlDesigner::ConnectionEditorStatements", "Custom"); struct Variable; struct MatchedFunction; diff --git a/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp b/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp index 61e19d1253d..8e1cd0ed6cb 100644 --- a/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp +++ b/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp @@ -105,10 +105,10 @@ void tst_ConnectionEditor::invalidSyntax() = "{someItem.complexCall(blah)}"; //valid QML bit not valid for ConnectionEditor QString result = ConnectionEditorEvaluator::getDisplayStringForType(statement1); - QCOMPARE(result, ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME); + QCOMPARE(result, ConnectionEditorStatements::CUSTOM_DISPLAY_NAME); result = ConnectionEditorEvaluator::getDisplayStringForType(statement2); - QCOMPARE(result, ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME); + QCOMPARE(result, ConnectionEditorStatements::CUSTOM_DISPLAY_NAME); auto resultHandler = ConnectionEditorEvaluator::parseStatement(statement1); auto parsedStatement = ConnectionEditorStatements::okStatement(resultHandler); @@ -169,14 +169,14 @@ void tst_ConnectionEditor::displayStrings_data() QTest::newRow("Custom function call assignment") << "{someItem.color = item.functionCall()}" - << ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME; + << ConnectionEditorStatements::CUSTOM_DISPLAY_NAME; QTest::newRow("Custom function call with argument") << "{someItem.color = item.functionCall(\"test\")}" - << ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME; + << ConnectionEditorStatements::CUSTOM_DISPLAY_NAME; QTest::newRow("Custom function call with argument 2") - << "{item.functionCall(\"test\")}" << ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME; + << "{item.functionCall(\"test\")}" << ConnectionEditorStatements::CUSTOM_DISPLAY_NAME; } void tst_ConnectionEditor::parseAssignment() From b0a9481049b631f4470fc00d6c292941c03eb72f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 15 Sep 2023 12:53:07 +0200 Subject: [PATCH 010/130] QmlDesigner: Remove qDebug from ConnectionView MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I495525286ff96331ce560d468bf1445fcf7a061e Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- .../connectioneditor/connectionmodel.cpp | 18 ++---------------- .../connectioneditor/propertytreemodel.cpp | 1 - 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 9b2102bbc3a..f2ef1ca02cc 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -652,7 +652,6 @@ QString generateDefaultStatement(ConnectionModelBackendDelegate::ActionType acti void ConnectionModelBackendDelegate::changeActionType(ActionType actionType) { - qDebug() << Q_FUNC_INFO << actionType; QTC_ASSERT(actionType != ConnectionModelStatementDelegate::Custom, return ); ConnectionModel *model = qobject_cast(parent()); @@ -676,9 +675,9 @@ void ConnectionModelBackendDelegate::changeActionType(ActionType actionType) QString statementSource = generateDefaultStatement(actionType, validId); auto tempHandler = ConnectionEditorEvaluator::parseStatement(statementSource); - qDebug() << ConnectionEditorStatements::toString(tempHandler); + auto newOkStatement = ConnectionEditorStatements::okStatement(tempHandler); - qDebug() << "newOk" << statementSource; + QTC_ASSERT(!ConnectionEditorStatements::isEmptyStatement(newOkStatement), return ); okStatement = newOkStatement; @@ -849,9 +848,6 @@ void ConnectionModelBackendDelegate::update() setupHandlerAndStatements(); - qDebug() << Q_FUNC_INFO << ConnectionEditorStatements::toString(m_handler) - << ConnectionEditorStatements::toJavascript(m_handler); - setupCondition(); QTC_ASSERT(model, return ); @@ -1440,7 +1436,6 @@ void ConnectionModelStatementDelegate::setupCallFunction() void ConnectionModelStatementDelegate::setupChangeState() { - qDebug() << Q_FUNC_INFO; QTC_ASSERT(std::holds_alternative(m_statement), return ); QTC_ASSERT(m_model->connectionView()->isAttached(), return ); @@ -1494,8 +1489,6 @@ void ConnectionModelStatementDelegate::setupStates() const auto stateSet = std::get(m_statement); - qDebug() << Q_FUNC_INFO << stateSet.stateName; - const QString nodeId = m_stateTargets.currentText(); const ModelNode node = m_model->connectionView()->modelNodeForId(nodeId); @@ -1752,20 +1745,16 @@ void ConditionListModel::command(const QString &string) //TODO remove from prodcution code QStringList list = string.split("%", Qt::SkipEmptyParts); - qDebug() << Q_FUNC_INFO << string << list.size(); - if (list.size() < 2) return; if (list.size() == 2) { if (list.first() == "A") { - qDebug() << "Append" << list.last(); appendToken(list.last()); } else if (list.first() == "R") { bool ok = true; int index = list.last().toInt(&ok); - qDebug() << "Remove" << index; if (ok) removeToken(index); } @@ -1778,15 +1767,12 @@ void ConditionListModel::command(const QString &string) if (ok) updateToken(index, list.last()); - qDebug() << "Update" << index << list.last(); } else if (list.first() == "I") { bool ok = true; int index = list.at(1).toInt(&ok); if (ok) insertToken(index, list.last()); - - qDebug() << "Insert" << index << list.last(); } } } diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index 7d4c55fb9d8..f9b9fddd640 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -520,7 +520,6 @@ const std::vector PropertyTreeModel::getDynamicProperties( const std::vector PropertyTreeModel::sortedAndFilteredPropertyNames( const NodeMetaInfo &metaInfo, bool recursive) const { - qDebug() << Q_FUNC_INFO << metaInfo.typeName() << recursive; auto filtered = Utils::filtered(metaInfo.properties(), [this, recursive](const PropertyMetaInfo &metaInfo) { // if (!metaInfo.isWritable()) - lhs/rhs From 954385c34d69ecddfb9e45ac55b3a88f11cde411 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 15 Sep 2023 14:36:36 +0200 Subject: [PATCH 011/130] QmlDesigner: Fix wrong role name Change-Id: I195ead9ade9c4bc430bdee630f533e6e04e15027 Reviewed-by: Thomas Hartmann --- .../qmldesigner/connectionseditor/ConnectionsDialogForm.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 56998fac9e0..6532af72681 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -79,7 +79,7 @@ Column { enabled: true } ListElement { - vvalue: ConnectionModelStatementDelegate.PrintMessage + value: ConnectionModelStatementDelegate.PrintMessage text: qsTr("Print Message") enabled: true } From b73b94d27a369626023dfccf2d2ac3ea4e6e3972 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 15 Sep 2023 14:37:02 +0200 Subject: [PATCH 012/130] QmlDesigner: Remove anchors from positioner child Change-Id: Ibff848d5f80ac74491ab77e88baffb4d0e1454bf Reviewed-by: Thomas Hartmann --- .../qmldesigner/connectionseditor/StatementEditor.qml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml index 3e7e552a28f..fc266fb4e9d 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml @@ -243,9 +243,7 @@ Column { PopupLabel { visible: root.actionType === ConnectionModelStatementDelegate.Custom text: qsTr("Custom Connections can only be edited with the binding editor") - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: 30 + width: root.width horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap } From f3036d52e526aef48f9e1864422295fa62ed79e1 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 15 Sep 2023 13:24:11 +0300 Subject: [PATCH 013/130] QmlDesigner: Fix dragging 3D snap configuration spinboxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed to use HelperWidgets.DoubleSpinBox, which has the proper drag support built into it and implemented required context functions. Fixes: QDS-10639 Change-Id: I6fce39251d7f754985f95bba5784784fc6eb8fae Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri Reviewed-by: Henning Gründl --- .../SnapConfigurationDialog.qml | 92 ++++++++++--------- .../components/edit3d/snapconfiguration.cpp | 48 +++++++++- .../components/edit3d/snapconfiguration.h | 7 +- 3 files changed, 100 insertions(+), 47 deletions(-) diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml index b246eb62daf..340570ff9b2 100644 --- a/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml @@ -20,13 +20,24 @@ Rectangle { border.color: StudioTheme.Values.themeControlOutline border.width: StudioTheme.Values.border - Connections { - target: rootView + function handlePosIntChanged() { + posIntSpin.value = posInt + } - // Spinboxes lose the initial binding if the value changes so we need these connections - onPosIntChanged: posIntSpin.realValue = rootView.posInt - onRotIntChanged: rotIntSpin.realValue = rootView.rotInt - onScaleIntChanged: scaleIntSpin.realValue = rootView.scaleInt + function handleRotIntChanged() { + rotIntSpin.value = rotInt + } + + function handleScaleIntChanged() { + scaleIntSpin.value = scaleInt + } + + // Connect context object signals to our handler functions + // Spinboxes lose the initial binding if the value changes so we need these handlers + Component.onCompleted: { + onPosIntChanged.connect(handlePosIntChanged); + onRotIntChanged.connect(handleRotIntChanged); + onScaleIntChanged.connect(handleScaleIntChanged); } ColumnLayout { @@ -94,7 +105,7 @@ Rectangle { Layout.column: 0 Layout.row: 1 Layout.minimumWidth: 100 - checked: rootView.posEnabled + checked: posEnabled actionIndicatorVisible: false hoverEnabled: true @@ -102,27 +113,26 @@ Rectangle { ToolTip.text: qsTr("Snap position.") ToolTip.delay: root.toolTipDelay - onToggled: rootView.posEnabled = checked + onToggled: posEnabled = checked } - StudioControls.RealSpinBox { + HelperWidgets.DoubleSpinBox { id: posIntSpin Layout.fillWidth: true Layout.column: 1 Layout.row: 1 Layout.leftMargin: 10 - realFrom: 1 - realTo: 10000 - realValue: rootView.posInt - realStepSize: 1 - actionIndicatorVisible: false + minimumValue: 1 + maximumValue: 10000 + value: posInt + stepSize: 1 + decimals: 0 - hoverEnabled: true - ToolTip.visible: hovered + ToolTip.visible: hover ToolTip.text: qsTr("Snap interval for move gizmo.") ToolTip.delay: root.toolTipDelay - onRealValueChanged: rootView.posInt = realValue + onValueModified: posInt = value } StudioControls.CheckBox { @@ -130,7 +140,7 @@ Rectangle { Layout.column: 0 Layout.row: 2 Layout.minimumWidth: 100 - checked: rootView.rotEnabled + checked: rotEnabled actionIndicatorVisible: false hoverEnabled: true @@ -138,27 +148,26 @@ Rectangle { ToolTip.text: qsTr("Snap rotation.") ToolTip.delay: root.toolTipDelay - onToggled: rootView.rotEnabled = checked + onToggled: rotEnabled = checked } - StudioControls.RealSpinBox { + HelperWidgets.DoubleSpinBox { id: rotIntSpin Layout.fillWidth: true Layout.column: 1 Layout.row: 2 Layout.leftMargin: 10 - realFrom: 1 - realTo: 90 - realValue: rootView.rotInt - realStepSize: 1 - actionIndicatorVisible: false + minimumValue: 1 + maximumValue: 90 + value: rotInt + stepSize: 1 + decimals: 0 - hoverEnabled: true - ToolTip.visible: hovered + ToolTip.visible: hover ToolTip.text: qsTr("Snap interval in degrees for rotation gizmo.") ToolTip.delay: root.toolTipDelay - onRealValueChanged: rootView.rotInt = realValue + onValueModified: rotInt = value } StudioControls.CheckBox { @@ -166,7 +175,7 @@ Rectangle { Layout.column: 0 Layout.row: 3 Layout.minimumWidth: 100 - checked: rootView.scaleEnabled + checked: scaleEnabled actionIndicatorVisible: false hoverEnabled: true @@ -174,27 +183,26 @@ Rectangle { ToolTip.text: qsTr("Snap scale.") ToolTip.delay: root.toolTipDelay - onToggled: rootView.scaleEnabled = checked + onToggled: scaleEnabled = checked } - StudioControls.RealSpinBox { + HelperWidgets.DoubleSpinBox { id: scaleIntSpin Layout.fillWidth: true Layout.column: 1 Layout.row: 3 Layout.leftMargin: 10 - realFrom: 1 - realTo: 100 - realValue: rootView.scaleInt - realStepSize: 1 - actionIndicatorVisible: false + minimumValue: 1 + maximumValue: 100 + value: scaleInt + stepSize: 1 + decimals: 0 - hoverEnabled: true - ToolTip.visible: hovered + ToolTip.visible: hover ToolTip.text: qsTr("Snap interval for scale gizmo in percentage of original scale.") ToolTip.delay: root.toolTipDelay - onRealValueChanged: rootView.scaleInt = realValue + onValueModified: scaleInt = value } StudioControls.CheckBox { @@ -204,7 +212,7 @@ Rectangle { Layout.column: 0 Layout.row: 4 Layout.columnSpan: 3 - checked: rootView.absolute + checked: absolute actionIndicatorVisible: false hoverEnabled: true @@ -212,7 +220,7 @@ Rectangle { ToolTip.text: qsTr("Toggles if the position snaps to absolute values or relative to object position.") ToolTip.delay: root.toolTipDelay - onToggled: rootView.absolute = checked + onToggled: absolute = checked } Text { @@ -236,7 +244,7 @@ Rectangle { text: qsTr("Reset All") Layout.bottomMargin: 8 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter - onClicked: rootView.resetDefaults() + onClicked: resetDefaults() } } } diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp index 44fb7ef1322..1412588f902 100644 --- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp @@ -11,7 +11,8 @@ #include -#include +#include +#include #include #include #include @@ -69,6 +70,8 @@ void SnapConfiguration::apply() m_view->syncSnapAuxPropsToSettings(); } + restoreCursor(); + cancel(); } @@ -83,6 +86,45 @@ void SnapConfiguration::resetDefaults() setScaleInt(defaultScaleInt); } +void SnapConfiguration::hideCursor() +{ + if (QGuiApplication::overrideCursor()) + return; + + QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); + + if (QWindow *w = QGuiApplication::focusWindow()) + m_lastPos = QCursor::pos(w->screen()); +} + +void SnapConfiguration::restoreCursor() +{ + if (!QGuiApplication::overrideCursor()) + return; + + QGuiApplication::restoreOverrideCursor(); + + if (QWindow *w = QGuiApplication::focusWindow()) + QCursor::setPos(w->screen(), m_lastPos); +} + +void SnapConfiguration::holdCursorInPlace() +{ + if (!QGuiApplication::overrideCursor()) + return; + + if (QWindow *w = QGuiApplication::focusWindow()) + QCursor::setPos(w->screen(), m_lastPos); +} + +int SnapConfiguration::devicePixelRatio() +{ + if (QWindow *w = QGuiApplication::focusWindow()) + return w->devicePixelRatio(); + + return 1; +} + void SnapConfiguration::showConfigDialog(const QPoint &pos) { bool posEnabled = Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION, true).toBool(); @@ -116,9 +158,7 @@ void SnapConfiguration::showConfigDialog(const QPoint &pos) m_configDialog->setModality(Qt::NonModal); m_configDialog->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); - m_configDialog->rootContext()->setContextProperties({ - {"rootView", QVariant::fromValue(this)} - }); + m_configDialog->rootContext()->setContextObject(this); m_configDialog->setSource(QUrl::fromLocalFile(path)); m_configDialog->installEventFilter(this); diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h index ae1a4a0d93b..b744e63053f 100644 --- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h @@ -5,11 +5,11 @@ #include #include +#include #include QT_BEGIN_NAMESPACE class QQuickView; -class QPoint; QT_END_NAMESPACE namespace QmlDesigner { @@ -47,6 +47,10 @@ public: ~SnapConfiguration(); Q_INVOKABLE void resetDefaults(); + Q_INVOKABLE void hideCursor(); + Q_INVOKABLE void restoreCursor(); + Q_INVOKABLE void holdCursorInPlace(); + Q_INVOKABLE int devicePixelRatio(); void cancel(); void apply(); @@ -98,6 +102,7 @@ private: double m_rotationInterval = 0.; double m_scaleInterval = 0.; bool m_changes = false; + QPoint m_lastPos; }; } // namespace QmlDesigner From 5ca59da1af343cf79d6e92bc72b3d9226f595dda Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 15 Sep 2023 13:23:00 +0200 Subject: [PATCH 014/130] McuSupport: Do not regularly restet code mode in QDS context Resetting the code model on a regular base has quite a negative performance impact and also generates unexpected code paths and updates. Change-Id: I5a62166d714beb6cc6c9a64333abf093a5a87ac6 Reviewed-by: Thomas Hartmann Reviewed-by: Yasser Grimes --- src/plugins/mcusupport/mcusupportplugin.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/plugins/mcusupport/mcusupportplugin.cpp b/src/plugins/mcusupport/mcusupportplugin.cpp index 05f457689d8..c0548554627 100644 --- a/src/plugins/mcusupport/mcusupportplugin.cpp +++ b/src/plugins/mcusupport/mcusupportplugin.cpp @@ -114,6 +114,13 @@ McuSupportPlugin::~McuSupportPlugin() dd = nullptr; } +static bool isQtDesignStudio() +{ + QSettings *settings = Core::ICore::settings(); + const QString qdsStandaloneEntry = "QML/Designer/StandAloneMode"; + return settings->value(qdsStandaloneEntry, false).toBool(); +} + void McuSupportPlugin::initialize() { setObjectName("McuSupportPlugin"); @@ -125,7 +132,10 @@ void McuSupportPlugin::initialize() // Temporary fix for CodeModel/Checker race condition // Remove after https://bugreports.qt.io/browse/QTCREATORBUG-29269 is closed - connect(QmlJS::ModelManagerInterface::instance(), + + if (!isQtDesignStudio()) { + connect( + QmlJS::ModelManagerInterface::instance(), &QmlJS::ModelManagerInterface::documentUpdated, [lasttime = QTime::currentTime()](QmlJS::Document::Ptr doc) mutable { // Prevent inifinite recall loop @@ -157,6 +167,7 @@ void McuSupportPlugin::initialize() ->action() ->trigger(); }); + } dd->m_options.registerQchFiles(); dd->m_options.registerExamples(); From b18b7920bbcd71829201a5b3f3be243bd9005faa Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Fri, 15 Sep 2023 14:16:20 +0200 Subject: [PATCH 015/130] Refuse to add properties from unselected nodes ... and select property after adding it to the model. Change-Id: I4210e9b1e27b3d396ba5f421629a2b90d2771f50 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../components/connectioneditor/bindingmodel.cpp | 12 ++++++++---- .../connectioneditor/dynamicpropertiesmodel.cpp | 11 ++++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index 94910d34d07..cc73f4b20a3 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -144,11 +144,15 @@ void BindingModel::setCurrentProperty(const AbstractProperty &property) void BindingModel::updateItem(const BindingProperty &property) { - if (auto *item = itemForProperty(property)) + if (auto *item = itemForProperty(property)) { item->updateProperty(property); - else - appendRow(new BindingModelItem(property)); - + } else { + ModelNode node = property.parentModelNode(); + if (connectionView()->isSelectedModelNode(node)) { + appendRow(new BindingModelItem(property)); + setCurrentProperty(property); + } + } m_delegate->update(currentProperty(), m_connectionView); } diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp index 69f51a277d2..560cecd3e1b 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp @@ -151,10 +151,15 @@ void DynamicPropertiesModel::updateItem(const AbstractProperty &property) if (!property.isDynamic()) return; - if (auto *item = itemForProperty(property)) + if (auto *item = itemForProperty(property)) { item->updateProperty(property); - else - addProperty(property); + } else { + ModelNode node = property.parentModelNode(); + if (selectedNodes().contains(node)) { + addProperty(property); + setCurrentProperty(property); + } + } } void DynamicPropertiesModel::removeItem(const AbstractProperty &property) From 1a1e898f0fa03fac8ad182d4bfe0b901686926cf Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 15 Sep 2023 13:37:33 +0200 Subject: [PATCH 016/130] QmlDesigner: Set Core::ICore::dialogParent() for QML windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ie09c7255495456cb76e94504392c9474e2a88688 Reviewed-by: Brook Cronin Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl Reviewed-by: Aleksei German Reviewed-by: --- .../qmldesignerbase/studio/studioquickwidget.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/plugins/qmldesignerbase/studio/studioquickwidget.cpp b/src/plugins/qmldesignerbase/studio/studioquickwidget.cpp index 2cfe5aa9741..b2c55c5b61d 100644 --- a/src/plugins/qmldesignerbase/studio/studioquickwidget.cpp +++ b/src/plugins/qmldesignerbase/studio/studioquickwidget.cpp @@ -3,8 +3,11 @@ #include "studioquickwidget.h" +#include + #include #include +#include #include QQmlEngine *s_engine = nullptr; @@ -46,6 +49,14 @@ void StudioQuickWidget::setResizeMode(QQuickWidget::ResizeMode mode) void StudioQuickWidget::setSource(const QUrl &url) { m_quickWidget->setSource(url); + + if (rootObject()) { + const auto windows = rootObject()->findChildren(); + + for (auto window : windows) { + window->setTransientParent(Core::ICore::dialogParent()->windowHandle()); + } + } } void StudioQuickWidget::refresh() {} From fedb38a0f5300c3d4ac1882ff85f678988e676f3 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 15 Sep 2023 15:11:08 +0200 Subject: [PATCH 017/130] QmlDesigner: Priotize 3D related properties Task-number: QDS-10682 Change-Id: Ib75877257ea34edfa539a198e56d9499810aca15 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../connectioneditor/propertytreemodel.cpp | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index f9b9fddd640..cd51def60a2 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -101,13 +101,43 @@ const std::vector priorityListSignals = {"clicked", "opacityChanged", "rotationChanged"}; -const std::vector priorityListProperties - = {"opacity", "visible", "value", "x", "y", - "width", "height", "rotation", "color", "scale", - "state", "enabled", "z", "text", "pressed", - "containsMouse", "checked", "hovered", "down", "clip", - "parent", "from", "radius", "smooth", "true", - "focus", "border.width", "border.color"}; +const std::vector priorityListProperties = {"opacity", + "visible", + "value", + "x", + "y", + "width", + "height", + "rotation", + "color", + "scale", + "state", + "enabled", + "z", + "text", + "pressed", + "containsMouse", + "checked", + "hovered", + "down", + "clip", + "parent", + "from", + "radius", + "smooth", + "true", + "focus", + "border.width", + "border.color", + "eulerRotation.x", + "eulerRotation.y", + "eulerRotation.z", + "scale.x", + "scale.y", + "scale.z", + "position.x", + "position.y", + "position.z"}; const std::vector priorityListSlots = {"toggle", "increase", From 55034b1cde123be6e500ca3ee5168d9200f0a1cd Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 15 Sep 2023 14:47:28 +0200 Subject: [PATCH 018/130] QmlDesigner: Pill redesign Change-Id: I3c28599c3cbc1555fbdb206e387dccf6f45a77cd Reviewed-by: Thomas Hartmann --- .../qmldesigner/connectionseditor/Pill.qml | 120 ++++++++++-------- .../imports/StudioTheme/Values.qml | 11 +- 2 files changed, 77 insertions(+), 54 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml index bbaefb16e4c..3e1a9bdcc3e 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml @@ -33,15 +33,10 @@ FocusScope { readonly property int margin: StudioTheme.Values.flowPillMargin property var operatorModel - width: { - if (root.isEditable()) { - if (root.isInvalid()) - return textInput.width + 1 + 2 * root.margin - else - return textInput.width + 1 - } - return textItem.contentWidth + icon.width + root.margin - } + property bool hovered: rootMouseArea.containsMouse + property bool selected: root.focus + + width: row.width height: StudioTheme.Values.flowPillHeight onActiveFocusChanged: { @@ -69,14 +64,34 @@ FocusScope { id: pill anchors.fill: parent color: { - if (root.isShadow()) - return StudioTheme.Values.themeInteraction - if (root.isEditable()) + if (root.isIntermediate()) return "transparent" - return StudioTheme.Values.themePillBackground + if (root.isShadow()) + return StudioTheme.Values.themePillShadowBackground + + if (root.isInvalid() && root.selected) + return StudioTheme.Values.themeWarning + + if (root.hovered) { + if (root.isOperator()) + return StudioTheme.Values.themePillOperatorBackgroundHover + if (root.isLiteral()) + return StudioTheme.Values.themePillLiteralBackgroundHover + + return StudioTheme.Values.themePillDefaultBackgroundHover + } + + if (root.isLiteral()) + return StudioTheme.Values.themePillLiteralBackgroundIdle + + if (root.isOperator()) + return StudioTheme.Values.themePillOperatorBackgroundIdle + + return StudioTheme.Values.themePillDefaultBackgroundIdle } - border.color: root.isInvalid() ? StudioTheme.Values.themeWarning : "white" // TODO colors + border.color: root.isInvalid() ? StudioTheme.Values.themeWarning + : StudioTheme.Values.themePillOutline border.width: { if (root.isShadow()) return 0 @@ -84,7 +99,7 @@ FocusScope { return 1 if (root.isEditable()) return 0 - if (rootMouseArea.containsMouse || root.focus) + if (root.selected) return 1 return 0 @@ -93,31 +108,60 @@ FocusScope { Row { id: row - anchors.left: parent.left - anchors.leftMargin: root.margin - anchors.verticalCenter: parent.verticalCenter - visible: root.isOperator() || root.isProperty() || root.isShadow() + leftPadding: root.margin + rightPadding: icon.visible ? 0 : root.margin Text { id: textItem + height: StudioTheme.Values.flowPillHeight + verticalAlignment: Text.AlignVCenter font.pixelSize: StudioTheme.Values.baseFontSize color: root.isShadow() ? StudioTheme.Values.themeTextSelectedTextColor : StudioTheme.Values.themeTextColor text: root.isOperator() ? root.operatorModel.convertValueToName(root.value) : root.value - anchors.verticalCenter: parent.verticalCenter + visible: root.isOperator() || root.isProperty() || root.isShadow() + } + + TextInput { + id: textInput + + x: root.isInvalid() ? root.margin : 0 + height: StudioTheme.Values.flowPillHeight + topPadding: 1 + font.pixelSize: StudioTheme.Values.baseFontSize + color: root.isInvalid() && root.selected ? StudioTheme.Values.themeTextSelectedTextColor + : StudioTheme.Values.themeTextColor + text: root.value + visible: !textItem.visible + enabled: root.isEditable() + + onEditingFinished: { + root.update(textInput.text) // emit + root.submit(textInput.cursorPosition) // emit + } + + Keys.onPressed: function (event) { + if (event.key === Qt.Key_Backspace) { + if (textInput.text !== "") + return + + root.remove() // emit + } + } } Item { id: icon - width: root.isShadow() ? root.margin : StudioTheme.Values.flowPillHeight + width: StudioTheme.Values.flowPillHeight height: StudioTheme.Values.flowPillHeight - visible: !root.isShadow() + visible: !root.isShadow() && !root.isIntermediate() Text { font.family: StudioTheme.Constants.iconFont.family font.pixelSize: StudioTheme.Values.smallIconFontSize - color: StudioTheme.Values.themeIconColor + color: root.isInvalid() && root.selected ? StudioTheme.Values.themeTextSelectedTextColor + : StudioTheme.Values.themeTextColor text: StudioTheme.Constants.close_small anchors.centerIn: parent } @@ -129,35 +173,5 @@ FocusScope { } } } - - TextInput { - id: textInput - - x: root.isInvalid() ? root.margin : 0 - height: StudioTheme.Values.flowPillHeight - topPadding: 1 - font.pixelSize: StudioTheme.Values.baseFontSize - color: (rootMouseArea.containsMouse || textInput.activeFocus) ? StudioTheme.Values.themeIconColor - : StudioTheme.Values.themeTextColor - text: root.value - visible: root.isEditable() - enabled: root.isEditable() - - //validator: RegularExpressionValidator { regularExpression: /^\S+/ } - - onEditingFinished: { - root.update(textInput.text) // emit - root.submit(textInput.cursorPosition) // emit - } - - Keys.onPressed: function (event) { - if (event.key === Qt.Key_Backspace) { - if (textInput.text !== "") - return - - root.remove() // emit - } - } - } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 43badf91300..c682395ed54 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -442,7 +442,16 @@ QtObject { property color themeDialogOutline: values.themeInteraction // Expression Builder - property color themePillBackground: Theme.color(Theme.DSdockWidgetSplitter) + property color themePillDefaultBackgroundIdle: "#ff000000" + property color themePillDefaultBackgroundHover: "#ff2c2c2c" + property color themePillOperatorBackgroundIdle: "#ff6b2a7b" + property color themePillOperatorBackgroundHover: "#ff7e478b" + property color themePillLiteralBackgroundIdle: "#ffb2005c" + property color themePillLiteralBackgroundHover: "#ffb24e81" + + property color themePillShadowBackground: values.themeInteraction + + property color themePillOutline: "#ffffffff" // Control Style Mapping From 9b2d6f5edd473541b1ccfcf665d55dd87c118223 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 15 Sep 2023 17:33:00 +0200 Subject: [PATCH 019/130] QmlDesigner: Fix Connections Popup layout * Set proper width on labels * Set proper width on StatementEditor * Forward backend property into StatementEditor Change-Id: Ia0db6ff7f235bd8d25eb0f36124fa158b76aaf04 Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../connectionseditor/BindingsDialogForm.qml | 13 ++++- .../ConnectionsDialogForm.qml | 17 +++++- .../connectionseditor/PopupLabel.qml | 2 +- .../PropertiesDialogForm.qml | 13 ++++- .../connectionseditor/StatementEditor.qml | 58 +++++++++++++++---- 5 files changed, 85 insertions(+), 18 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml index 157d0d35177..c444512967f 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml @@ -22,8 +22,17 @@ Column { Row { spacing: root.horizontalSpacing - PopupLabel { text: qsTr("From") ; tooltip: qsTr("The Property to assign from.")} - PopupLabel { text: qsTr("To"); tooltip: qsTr("The Property to assign to.") } + PopupLabel { + width: root.columnWidth + text: qsTr("From") + tooltip: qsTr("The Property to assign from.") + } + + PopupLabel { + width: root.columnWidth + text: qsTr("To") + tooltip: qsTr("The Property to assign to.") + } } Row { diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 6532af72681..4b2e45ee166 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -27,8 +27,17 @@ Column { Row { spacing: root.horizontalSpacing - PopupLabel { text: qsTr("Signal"); tooltip: qsTr("The name of the signal.") } - PopupLabel { text: qsTr("Action"); tooltip: qsTr("The type of the action.") } + PopupLabel { + width: root.columnWidth + text: qsTr("Signal") + tooltip: qsTr("The name of the signal.") + } + + PopupLabel { + width: root.columnWidth + text: qsTr("Action") + tooltip: qsTr("The type of the action.") + } } Row { @@ -93,10 +102,12 @@ Column { } StatementEditor { + width: root.width actionType: action.currentValue ?? ConnectionModelStatementDelegate.Custom horizontalSpacing: root.horizontalSpacing columnWidth: root.columnWidth statement: backend.okStatement + backend: root.backend spacing: root.verticalSpacing } @@ -190,10 +201,12 @@ Column { //Else Statement StatementEditor { + width: root.width actionType: action.currentValue ?? ConnectionModelStatementDelegate.Custom horizontalSpacing: root.horizontalSpacing columnWidth: root.columnWidth statement: backend.koStatement + backend: root.backend spacing: root.verticalSpacing visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && backend.hasCondition && backend.hasElse diff --git a/share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml b/share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml index 6e5a117d8ce..6f8cb1ea06a 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml @@ -8,10 +8,10 @@ import StudioControls as StudioControls import StudioTheme as StudioTheme Text { - width: root.columnWidth color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.myFontSize property alias tooltip: area.tooltip + HelperWidgets.ToolTipArea { id: area anchors.fill: parent diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml index 26398e7acbb..1c1f8db76bf 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml @@ -38,8 +38,17 @@ Column { Row { spacing: root.horizontalSpacing - PopupLabel { text: qsTr("Name") ; tooltip: qsTr("The name of the property.")} - PopupLabel { text: qsTr("Value"); tooltip: qsTr("The value of the property.") } + PopupLabel { + width: root.columnWidth + text: qsTr("Name") + tooltip: qsTr("The name of the property.") + } + + PopupLabel { + width: root.columnWidth + text: qsTr("Value") + tooltip: qsTr("The value of the property.") + } } Row { diff --git a/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml index fc266fb4e9d..c6f8a412b5b 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml @@ -18,16 +18,24 @@ Column { property var statement - //implicitWidth: Math.max(16, container.childrenRect.width + container.childrenRect.x) - //implicitHeight: Math.max(16, container.childrenRect.height + container.childrenRect.y) + property var backend // Call Function Row { visible: root.actionType === ConnectionModelStatementDelegate.CallFunction spacing: root.horizontalSpacing - PopupLabel { text: qsTr("Item") ; tooltip: qsTr("The target item of the function.")} - PopupLabel { text: qsTr("Method") ; tooltip: qsTr("The name of the function.")} + PopupLabel { + width: root.columnWidth + text: qsTr("Item") + tooltip: qsTr("The target item of the function.") + } + + PopupLabel { + width: root.columnWidth + text: qsTr("Method") + tooltip: qsTr("The name of the function.") + } } Row { @@ -46,7 +54,7 @@ Column { onCurrentTypeIndexChanged: functionId.currentIndex = functionId.currentTypeIndex } - StudioControls.TopLevelComboBox { + StudioControls.TopLevelComboBox { id: functionName style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth @@ -63,8 +71,16 @@ Column { visible: root.actionType === ConnectionModelStatementDelegate.Assign spacing: root.horizontalSpacing - PopupLabel { text: qsTr("From") ; tooltip: qsTr("The Property to assign from.")} - PopupLabel { text: qsTr("To"); tooltip: qsTr("The Property to assign to.") } + PopupLabel { + width: root.columnWidth + text: qsTr("From") + tooltip: qsTr("The Property to assign from.") + } + PopupLabel { + width: root.columnWidth + text: qsTr("To") + tooltip: qsTr("The Property to assign to.") + } } Row { @@ -132,8 +148,17 @@ Column { visible: root.actionType === ConnectionModelStatementDelegate.ChangeState spacing: root.horizontalSpacing - PopupLabel { text: qsTr("State Group"); tooltip: qsTr("The State Group.") } - PopupLabel { text: qsTr("State"); tooltip: qsTr("The State .") } + PopupLabel { + width: root.columnWidth + text: qsTr("State Group") + tooltip: qsTr("The State Group.") + } + + PopupLabel { + width: root.columnWidth + text: qsTr("State") + tooltip: qsTr("The State .") + } } Row { @@ -169,8 +194,17 @@ Column { visible: root.actionType === ConnectionModelStatementDelegate.SetProperty spacing: root.horizontalSpacing - PopupLabel { text: qsTr("Item"); tooltip: qsTr("The Item.")} - PopupLabel { text: qsTr("Property"); tooltip: qsTr("The property of the item.")} + PopupLabel { + width: root.columnWidth + text: qsTr("Item") + tooltip: qsTr("The Item.") + } + + PopupLabel { + width: root.columnWidth + text: qsTr("Property") + tooltip: qsTr("The property of the item.") + } } Row { @@ -203,6 +237,7 @@ Column { } PopupLabel { + width: root.columnWidth visible: root.actionType === ConnectionModelStatementDelegate.SetProperty text: qsTr("Value") } @@ -222,6 +257,7 @@ Column { // Print Message PopupLabel { + width: root.columnWidth visible: root.actionType === ConnectionModelStatementDelegate.PrintMessage text: qsTr("Message") tooltip: qsTr("The message that is printed.") From 5aa3e573cedb98464f829a43212569b380466b27 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 15 Sep 2023 17:34:38 +0200 Subject: [PATCH 020/130] QmlDesigner: Fix empty literal remove Fix issue that literal token is not immediately when pressing backspace and text is empty. Change-Id: I7ba2401ad6adfa689f697188feb7b30a71d82eeb Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- share/qtcreator/qmldesigner/connectionseditor/Pill.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml index 3e1a9bdcc3e..c067fb5b162 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml @@ -141,7 +141,7 @@ FocusScope { root.submit(textInput.cursorPosition) // emit } - Keys.onPressed: function (event) { + Keys.onReleased: function (event) { if (event.key === Qt.Key_Backspace) { if (textInput.text !== "") return From a496ac5d020c8254448ad5c8e3f464e1d53363ff Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Fri, 15 Sep 2023 13:28:28 +0200 Subject: [PATCH 021/130] QmlDesigner: Design fixes for connections popout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I06968cfef23f8b296f1ba6b07e57cdc96701447b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl --- .../ConnectionsDialogForm.qml | 4 +- .../qmldesigner/connectionseditor/Pill.qml | 21 +++++++--- .../connectionseditor/PopupDialog.qml | 9 ++++- .../connectionseditor/SuggestionPopup.qml | 17 ++++---- .../imports/StudioTheme/Values.qml | 21 ++++++---- share/qtcreator/themes/dark.creatortheme | 39 ++++++++++++++++++- share/qtcreator/themes/default.creatortheme | 39 ++++++++++++++++++- .../themes/design-light.creatortheme | 12 ++++++ share/qtcreator/themes/design.creatortheme | 16 +++++++- share/qtcreator/themes/flat-dark.creatortheme | 39 ++++++++++++++++++- .../qtcreator/themes/flat-light.creatortheme | 39 ++++++++++++++++++- share/qtcreator/themes/flat.creatortheme | 39 ++++++++++++++++++- src/libs/utils/theme/theme.h | 11 ++++++ 13 files changed, 272 insertions(+), 34 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 4b2e45ee166..057e08e305b 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -11,7 +11,7 @@ Column { id: root readonly property real horizontalSpacing: 10 - readonly property real verticalSpacing: 16 + readonly property real verticalSpacing: 12 readonly property real columnWidth: (root.width - root.horizontalSpacing) / 2 property var backend @@ -217,7 +217,7 @@ Column { id: editor width: parent.width height: 150 - color: StudioTheme.Values.themeToolbarBackground + color: StudioTheme.Values.themeConnectionCodeEditor Text { id: code diff --git a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml index c067fb5b162..3862e53e5ce 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml @@ -116,8 +116,8 @@ FocusScope { height: StudioTheme.Values.flowPillHeight verticalAlignment: Text.AlignVCenter font.pixelSize: StudioTheme.Values.baseFontSize - color: root.isShadow() ? StudioTheme.Values.themeTextSelectedTextColor - : StudioTheme.Values.themeTextColor + color: root.isShadow() ? StudioTheme.Values.themePillTextSelected + : StudioTheme.Values.themePillText text: root.isOperator() ? root.operatorModel.convertValueToName(root.value) : root.value visible: root.isOperator() || root.isProperty() || root.isShadow() @@ -130,8 +130,17 @@ FocusScope { height: StudioTheme.Values.flowPillHeight topPadding: 1 font.pixelSize: StudioTheme.Values.baseFontSize - color: root.isInvalid() && root.selected ? StudioTheme.Values.themeTextSelectedTextColor - : StudioTheme.Values.themeTextColor + color: { + if (root.isIntermediate()) + return StudioTheme.Values.themePillTextEdit + if (root.isInvalid() && root.selected) + return StudioTheme.Values.themePillTextSelected + return StudioTheme.Values.themePillText + } + + selectedTextColor:StudioTheme.Values.themePillTextSelected + selectionColor: StudioTheme.Values.themeInteraction + text: root.value visible: !textItem.visible enabled: root.isEditable() @@ -160,8 +169,8 @@ FocusScope { Text { font.family: StudioTheme.Constants.iconFont.family font.pixelSize: StudioTheme.Values.smallIconFontSize - color: root.isInvalid() && root.selected ? StudioTheme.Values.themeTextSelectedTextColor - : StudioTheme.Values.themeTextColor + color: root.isInvalid() && root.selected ? StudioTheme.Values.themePillTextSelected + : StudioTheme.Values.themePillText text: StudioTheme.Constants.close_small anchors.centerIn: parent } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml index 9d17218b00a..da911854ada 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml @@ -14,11 +14,16 @@ Window { default property alias content: mainContent.children property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle - width: 320 + width: 300 height: column.implicitHeight visible: true flags: Qt.FramelessWindowHint | Qt.Dialog - color: StudioTheme.Values.themePopoutBackground + + Rectangle { + anchors.fill: parent + color: StudioTheme.Values.themePopoutBackground + border.color: "#636363"//StudioTheme.Values.themeTextColor + } function ensureVerticalPosition() { if ((window.y + window.height) > (Screen.height - window.style.dialogScreenMargin)) { diff --git a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml index 735bc9364cd..91945ded88e 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml @@ -71,7 +71,7 @@ Controls.Popup { width: stack.width height: 30 visible: root.listModel.parentName !== "" - color: backMouseArea.containsMouse ? "#4DBFFF" : "transparent" + color: backMouseArea.containsMouse ? StudioTheme.Values.themeInteraction : "transparent" MouseArea { id: backMouseArea @@ -95,7 +95,8 @@ Controls.Popup { id: chevronLeft font.family: StudioTheme.Constants.iconFont.family font.pixelSize: root.style.baseIconFontSize - color: backMouseArea.containsMouse ? "#111111" : "white" // TODO colors + color: backMouseArea.containsMouse ? StudioTheme.Values.themeTextSelectedTextColor + : StudioTheme.Values.themeTextColor text: StudioTheme.Constants.back_medium anchors.centerIn: parent } @@ -104,7 +105,8 @@ Controls.Popup { Text { anchors.verticalCenter: parent.verticalCenter text: root.listModel.parentName - color: backMouseArea.containsMouse ? "#111111" : "white" // TODO colors + color: backMouseArea.containsMouse ? StudioTheme.Values.themeTextSelectedTextColor + : StudioTheme.Values.themeTextColor horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -272,12 +274,9 @@ Controls.Popup { width: textItem.contentWidth + 2 * StudioTheme.Values.flowPillMargin height: StudioTheme.Values.flowPillHeight - color: "#161616" + color: mouseArea.containsMouse ? StudioTheme.Values.themePillOperatorBackgroundHover + : StudioTheme.Values.themePillOperatorBackgroundIdle radius: StudioTheme.Values.flowPillRadius - border { - color: "white" - width: mouseArea.containsMouse ? 1 : 0 - } HelperWidgets.ToolTipArea { id: mouseArea @@ -291,7 +290,7 @@ Controls.Popup { Text { id: textItem font.pixelSize: StudioTheme.Values.baseFontSize - color: StudioTheme.Values.themeTextColor + color: StudioTheme.Values.themePillText text: delegate.name anchors.centerIn: parent } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index c682395ed54..31b9fc71e85 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -221,7 +221,7 @@ QtObject { property real colorEditorPopupSpinBoxWidth: 54 // Popup Window - property real titleBarHeight: values.height + 10 + property real titleBarHeight: values.height + 20 property real popupMargin: 10 // Toolbar @@ -339,6 +339,7 @@ QtObject { // Panels & Panes property color themeBackgroundColorNormal: Theme.color(Theme.DSBackgroundColorNormal) property color themeBackgroundColorAlternate: Theme.color(Theme.DSBackgroundColorAlternate) + property color themeConnectionCodeEditor: Theme.color(Theme.DSconnectionCodeEditor) // Text colors property color themeTextColor: Theme.color(Theme.DStextColor) @@ -442,17 +443,23 @@ QtObject { property color themeDialogOutline: values.themeInteraction // Expression Builder - property color themePillDefaultBackgroundIdle: "#ff000000" - property color themePillDefaultBackgroundHover: "#ff2c2c2c" - property color themePillOperatorBackgroundIdle: "#ff6b2a7b" - property color themePillOperatorBackgroundHover: "#ff7e478b" - property color themePillLiteralBackgroundIdle: "#ffb2005c" - property color themePillLiteralBackgroundHover: "#ffb24e81" + property color themePillDefaultBackgroundIdle: Theme.color(Theme.DSpillDefaultBackgroundIdle) + property color themePillDefaultBackgroundHover: Theme.color(Theme.DSpillDefaultBackgroundHover) + property color themePillOperatorBackgroundIdle: Theme.color(Theme.DSpillOperatorBackgroundIdle) + property color themePillOperatorBackgroundHover: Theme.color(Theme.DSpillOperatorBackgroundHover) + property color themePillLiteralBackgroundIdle: Theme.color(Theme.DSpillLiteralBackgroundIdle) + property color themePillLiteralBackgroundHover: Theme.color(Theme.DSpillLiteralBackgroundHover) property color themePillShadowBackground: values.themeInteraction property color themePillOutline: "#ffffffff" + property color themePillText: Theme.color(Theme.DSpillText) + property color themePillTextSelected: Theme.color(Theme.DSpillTextSelected) + property color themePillTextEdit: Theme.color(Theme.DspillTextEdit) + + + // Control Style Mapping property ControlStyle controlStyle: DefaultStyle {} diff --git a/share/qtcreator/themes/dark.creatortheme b/share/qtcreator/themes/dark.creatortheme index a214688bf90..adec8cd6285 100644 --- a/share/qtcreator/themes/dark.creatortheme +++ b/share/qtcreator/themes/dark.creatortheme @@ -75,9 +75,44 @@ DSstateBackgroundColor_hover=ashGrey DSstateControlBackgroundColor_globalHover=ashGrey DSstateControlBackgroundColor_hover=raincloudGrey DSpanelBackground=dawnGrey -;END NEW FOR QtDS 4.0 -DSpanelBackground=dawnGrey +;4.3 +DSpopoutBackground=offBlack +DSpopoutControlBackground_idle=offBlack +DSpopoutControlBackground_hover=dawnGrey +DSpopoutControlBackground_globalHover=dawnGrey +DSpopoutControlBackground_interaction=highlightBlue +DSpopoutControlBackground_disabled=offBlack +DSpopoutPopupBackground=nearBlack + +DSpopoutControlBorder_idle=midnightGrey +DSpopoutControlBorder_hover=raincloudGrey +DSpopoutControlBorder_interaction=highlightBlue +DSpopoutControlBorder_disabled=offBlack + +DSpopoutButtonBackground_idle=offBlack +DSpopoutButtonBackground_hover=dawnGrey +DSpopoutButtonBackground_interaction=highlightBlue +DSpopoutButtonBackground_disabled=offBlack + +DSpopoutButtonBorder_idle=slateGrey +DSpopoutButtonBorder_hover=lightWhite +DSpopoutButtonBorder_interaction=highlightBlue +DSpopoutButtonBorder_disabled=offBlack + +;4.4 +DSconnectionCodeEditor=midnightGrey +DSpillText=fullWhite +DSpillTextSelected=fullBlack +DspillTextEdit=fullWhite +DSpillDefaultBackgroundIdle=graniteGrey +DSpillDefaultBackgroundHover=raincloudGrey +DSpillOperatorBackgroundIdle=ff6b2a7b +DSpillOperatorBackgroundHover=ff7e478b +DSpillLiteralBackgroundIdle=ff447953 +DSpillLiteralBackgroundHover=ff61866B + +;END NEW FOR QtDS 4 DSwelcomeScreenBackground=ff242424 DSsubPanelBackground=ff1c1c1c diff --git a/share/qtcreator/themes/default.creatortheme b/share/qtcreator/themes/default.creatortheme index a7095266a8b..06f61831843 100644 --- a/share/qtcreator/themes/default.creatortheme +++ b/share/qtcreator/themes/default.creatortheme @@ -68,7 +68,44 @@ DSstateBackgroundColor_hover=concreteGrey DSstateControlBackgroundColor_globalHover=concreteGrey DSstateControlBackgroundColor_hover=smokeGrey DSpanelBackground=dawnGrey -;END NEW FOR QtDS 4.0 + +;4.3 +DSpopoutBackground=offWhite +DSpopoutControlBackground_idle=offWhite +DSpopoutControlBackground_hover=lightWhite +DSpopoutControlBackground_globalHover=lightWhite +DSpopoutControlBackground_interaction=highlightBlue +DSpopoutControlBackground_disabled=offWhite +DSpopoutPopupBackground=lightWhite + +DSpopoutControlBorder_idle=slateGrey +DSpopoutControlBorder_hover=concreteGrey +DSpopoutControlBorder_interaction=highlightBlue +DSpopoutControlBorder_disabled=offWhite + +DSpopoutButtonBackground_idle=offWhite +DSpopoutButtonBackground_hover=lightWhite +DSpopoutButtonBackground_interaction=highlightBlue +DSpopoutButtonBackground_disabled=offWhite + +DSpopoutButtonBorder_idle=smokeGrey +DSpopoutButtonBorder_hover=shadowGrey +DSpopoutButtonBorder_interaction=highlightBlue +DSpopoutButtonBorder_disabled=offWhite + +;4.4 +DSconnectionCodeEditor=lightWhite +DSpillText=fullWhite +DSpillTextSelected=fullBlack +DspillTextEdit=fullBlack +DSpillDefaultBackgroundIdle=shadowGrey +DSpillDefaultBackgroundHover=smokeGrey +DSpillOperatorBackgroundIdle=ff6b2a7b +DSpillOperatorBackgroundHover=ff7e478b +DSpillLiteralBackgroundIdle=ff447953 +DSpillLiteralBackgroundHover=ff61866B + +;END NEW FOR QtDS 4 DSpanelBackground=ffeaeaea diff --git a/share/qtcreator/themes/design-light.creatortheme b/share/qtcreator/themes/design-light.creatortheme index 46fc695570a..5c9a0c6277a 100644 --- a/share/qtcreator/themes/design-light.creatortheme +++ b/share/qtcreator/themes/design-light.creatortheme @@ -107,6 +107,18 @@ DSpopoutButtonBorder_hover=shadowGrey DSpopoutButtonBorder_interaction=highlightBlue DSpopoutButtonBorder_disabled=offWhite +;4.4 +DSconnectionCodeEditor=lightWhite +DSpillText=fullWhite +DSpillTextSelected=fullBlack +DspillTextEdit=fullBlack +DSpillDefaultBackgroundIdle=shadowGrey +DSpillDefaultBackgroundHover=smokeGrey +DSpillOperatorBackgroundIdle=ff6b2a7b +DSpillOperatorBackgroundHover=ff7e478b +DSpillLiteralBackgroundIdle=ff447953 +DSpillLiteralBackgroundHover=ff61866B + ;END NEW FOR QtDS 4 DSpanelBackground=ffeaeaea diff --git a/share/qtcreator/themes/design.creatortheme b/share/qtcreator/themes/design.creatortheme index 1d8295145e4..f2a12766b9d 100644 --- a/share/qtcreator/themes/design.creatortheme +++ b/share/qtcreator/themes/design.creatortheme @@ -90,8 +90,8 @@ DSpopoutControlBackground_interaction=highlightBlue DSpopoutControlBackground_disabled=offBlack DSpopoutPopupBackground=nearBlack -DSpopoutControlBorder_idle=nearBlack -DSpopoutControlBorder_hover=midnightGrey +DSpopoutControlBorder_idle=midnightGrey +DSpopoutControlBorder_hover=raincloudGrey DSpopoutControlBorder_interaction=highlightBlue DSpopoutControlBorder_disabled=offBlack @@ -105,6 +105,18 @@ DSpopoutButtonBorder_hover=lightWhite DSpopoutButtonBorder_interaction=highlightBlue DSpopoutButtonBorder_disabled=offBlack +;4.4 +DSconnectionCodeEditor=midnightGrey +DSpillText=fullWhite +DSpillTextSelected=fullBlack +DspillTextEdit=fullWhite +DSpillDefaultBackgroundIdle=graniteGrey +DSpillDefaultBackgroundHover=raincloudGrey +DSpillOperatorBackgroundIdle=ff6b2a7b +DSpillOperatorBackgroundHover=ff7e478b +DSpillLiteralBackgroundIdle=ff447953 +DSpillLiteralBackgroundHover=ff61866B + ;END NEW FOR QtDS 4 ;REMAPPED diff --git a/share/qtcreator/themes/flat-dark.creatortheme b/share/qtcreator/themes/flat-dark.creatortheme index bb6b905288c..8f32e7dee9e 100644 --- a/share/qtcreator/themes/flat-dark.creatortheme +++ b/share/qtcreator/themes/flat-dark.creatortheme @@ -80,7 +80,44 @@ DSstateBackgroundColor_hover=ashGrey DSstateControlBackgroundColor_globalHover=ashGrey DSstateControlBackgroundColor_hover=raincloudGrey DSpanelBackground=dawnGrey -;END NEW FOR QtDS 4.0 + +;4.3 +DSpopoutBackground=offBlack +DSpopoutControlBackground_idle=offBlack +DSpopoutControlBackground_hover=dawnGrey +DSpopoutControlBackground_globalHover=dawnGrey +DSpopoutControlBackground_interaction=highlightBlue +DSpopoutControlBackground_disabled=offBlack +DSpopoutPopupBackground=nearBlack + +DSpopoutControlBorder_idle=midnightGrey +DSpopoutControlBorder_hover=raincloudGrey +DSpopoutControlBorder_interaction=highlightBlue +DSpopoutControlBorder_disabled=offBlack + +DSpopoutButtonBackground_idle=offBlack +DSpopoutButtonBackground_hover=dawnGrey +DSpopoutButtonBackground_interaction=highlightBlue +DSpopoutButtonBackground_disabled=offBlack + +DSpopoutButtonBorder_idle=slateGrey +DSpopoutButtonBorder_hover=lightWhite +DSpopoutButtonBorder_interaction=highlightBlue +DSpopoutButtonBorder_disabled=offBlack + +;4.4 +DSconnectionCodeEditor=midnightGrey +DSpillText=fullWhite +DSpillTextSelected=fullBlack +DspillTextEdit=fullWhite +DSpillDefaultBackgroundIdle=graniteGrey +DSpillDefaultBackgroundHover=raincloudGrey +DSpillOperatorBackgroundIdle=ff6b2a7b +DSpillOperatorBackgroundHover=ff7e478b +DSpillLiteralBackgroundIdle=ff447953 +DSpillLiteralBackgroundHover=ff61866B + +;END NEW FOR QtDS 4 DSwelcomeScreenBackground=ff242424 DSsubPanelBackground=ff1c1c1c diff --git a/share/qtcreator/themes/flat-light.creatortheme b/share/qtcreator/themes/flat-light.creatortheme index cbf4b09ce17..139dc9c6479 100644 --- a/share/qtcreator/themes/flat-light.creatortheme +++ b/share/qtcreator/themes/flat-light.creatortheme @@ -77,7 +77,44 @@ DSstateBackgroundColor_hover=concreteGrey DSstateControlBackgroundColor_globalHover=concreteGrey DSstateControlBackgroundColor_hover=smokeGrey DSpanelBackground=dawnGrey -;END NEW FOR QtDS 4.0 + +;4.3 +DSpopoutBackground=offWhite +DSpopoutControlBackground_idle=offWhite +DSpopoutControlBackground_hover=lightWhite +DSpopoutControlBackground_globalHover=lightWhite +DSpopoutControlBackground_interaction=highlightBlue +DSpopoutControlBackground_disabled=offWhite +DSpopoutPopupBackground=lightWhite + +DSpopoutControlBorder_idle=slateGrey +DSpopoutControlBorder_hover=concreteGrey +DSpopoutControlBorder_interaction=highlightBlue +DSpopoutControlBorder_disabled=offWhite + +DSpopoutButtonBackground_idle=offWhite +DSpopoutButtonBackground_hover=lightWhite +DSpopoutButtonBackground_interaction=highlightBlue +DSpopoutButtonBackground_disabled=offWhite + +DSpopoutButtonBorder_idle=smokeGrey +DSpopoutButtonBorder_hover=shadowGrey +DSpopoutButtonBorder_interaction=highlightBlue +DSpopoutButtonBorder_disabled=offWhite + +;4.4 +DSconnectionCodeEditor=lightWhite +DSpillText=fullWhite +DSpillTextSelected=fullBlack +DspillTextEdit=fullBlack +DSpillDefaultBackgroundIdle=shadowGrey +DSpillDefaultBackgroundHover=smokeGrey +DSpillOperatorBackgroundIdle=ff6b2a7b +DSpillOperatorBackgroundHover=ff7e478b +DSpillLiteralBackgroundIdle=ff447953 +DSpillLiteralBackgroundHover=ff61866B + +;END NEW FOR QtDS 4 DSpanelBackground=ffeaeaea diff --git a/share/qtcreator/themes/flat.creatortheme b/share/qtcreator/themes/flat.creatortheme index a252562a56f..9ace50b236c 100644 --- a/share/qtcreator/themes/flat.creatortheme +++ b/share/qtcreator/themes/flat.creatortheme @@ -74,7 +74,44 @@ DSstateBackgroundColor_hover=ashGrey DSstateControlBackgroundColor_globalHover=ashGrey DSstateControlBackgroundColor_hover=raincloudGrey DSpanelBackground=dawnGrey -;END NEW FOR QtDS 4.0 + +;4.3 +DSpopoutBackground=offBlack +DSpopoutControlBackground_idle=offBlack +DSpopoutControlBackground_hover=dawnGrey +DSpopoutControlBackground_globalHover=dawnGrey +DSpopoutControlBackground_interaction=highlightBlue +DSpopoutControlBackground_disabled=offBlack +DSpopoutPopupBackground=nearBlack + +DSpopoutControlBorder_idle=midnightGrey +DSpopoutControlBorder_hover=raincloudGrey +DSpopoutControlBorder_interaction=highlightBlue +DSpopoutControlBorder_disabled=offBlack + +DSpopoutButtonBackground_idle=offBlack +DSpopoutButtonBackground_hover=dawnGrey +DSpopoutButtonBackground_interaction=highlightBlue +DSpopoutButtonBackground_disabled=offBlack + +DSpopoutButtonBorder_idle=slateGrey +DSpopoutButtonBorder_hover=lightWhite +DSpopoutButtonBorder_interaction=highlightBlue +DSpopoutButtonBorder_disabled=offBlack + +;4.4 +DSconnectionCodeEditor=midnightGrey +DSpillText=fullWhite +DSpillTextSelected=fullBlack +DspillTextEdit=fullWhite +DSpillDefaultBackgroundIdle=graniteGrey +DSpillDefaultBackgroundHover=raincloudGrey +DSpillOperatorBackgroundIdle=ff6b2a7b +DSpillOperatorBackgroundHover=ff7e478b +DSpillLiteralBackgroundIdle=ff447953 +DSpillLiteralBackgroundHover=ff61866B + +;END NEW FOR QtDS 4 DSwelcomeScreenBackground=ff242424 DSsubPanelBackground=ff1c1c1c diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h index c3aa8842818..0dbdb61fbe4 100644 --- a/src/libs/utils/theme/theme.h +++ b/src/libs/utils/theme/theme.h @@ -345,6 +345,17 @@ public: DSscrollBarTrack, DSscrollBarHandle, DSscrollBarHandle_idle, + DSconnectionCodeEditor, + DSpillText, + DSpillTextSelected, + DspillTextEdit, + DSpillDefaultBackgroundIdle, + DSpillDefaultBackgroundHover, + DSpillOperatorBackgroundIdle, + DSpillOperatorBackgroundHover, + DSpillLiteralBackgroundIdle, + DSpillLiteralBackgroundHover, + /*Legacy QtDS*/ DSiconColor, From f25ce01ead2482945ef5cb70294c64f7ac1c209e Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 15 Sep 2023 21:05:02 +0200 Subject: [PATCH 022/130] QmlDesigner: Close dialog when view loses focus Change-Id: I4666953ff862886fbb02fb3982d6856a223fe969 Reviewed-by: Brook Cronin Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/connectionseditor/BindingsListView.qml | 5 +++++ .../qmldesigner/connectionseditor/ConnectionsListView.qml | 5 +++++ .../qmldesigner/connectionseditor/PropertiesListView.qml | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml index 61433bc3ef5..f5aa2cd1652 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml @@ -15,6 +15,11 @@ ListView { property bool adsFocus: false + onAdsFocusChanged: { + if (!root.adsFocus) + dialog.close() + } + clip: true interactive: true highlightMoveDuration: 0 diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml index 3d5e1edd5c0..f38bdf9abfe 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml @@ -15,6 +15,11 @@ ListView { property bool adsFocus: false + onAdsFocusChanged: { + if (!root.adsFocus) + dialog.close() + } + clip: true interactive: true highlightMoveDuration: 0 diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index c731e5ac819..b9a45c76db5 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -15,6 +15,11 @@ ListView { property bool adsFocus: false + onAdsFocusChanged: { + if (!root.adsFocus) + dialog.close() + } + clip: true interactive: true highlightMoveDuration: 0 From 44699727adff0ab4300266e3b01f749c72aa1089 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 18 Sep 2023 11:57:22 +0200 Subject: [PATCH 023/130] QmlDesigner: Close dialog when view loses focus Change-Id: Ib48229615ee9b2ccef1a8be98086a93851dae6af Reviewed-by: Thomas Hartmann --- .../qmldesigner/connectionseditor/BindingsListView.qml | 9 +++++---- .../connectionseditor/ConnectionsListView.qml | 9 +++++---- .../qmldesigner/connectionseditor/PropertiesListView.qml | 9 +++++---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml index f5aa2cd1652..83b2322e37b 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml @@ -15,10 +15,11 @@ ListView { property bool adsFocus: false - onAdsFocusChanged: { - if (!root.adsFocus) - dialog.close() - } + // Temporarily remove due to dockwidget focus issue + //onAdsFocusChanged: { + // if (!root.adsFocus) + // dialog.close() + //} clip: true interactive: true diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml index f38bdf9abfe..a8159056fc0 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml @@ -15,10 +15,11 @@ ListView { property bool adsFocus: false - onAdsFocusChanged: { - if (!root.adsFocus) - dialog.close() - } + // Temporarily remove due to dockwidget focus issue + //onAdsFocusChanged: { + // if (!root.adsFocus) + // dialog.close() + //} clip: true interactive: true diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index b9a45c76db5..c6fdf333333 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -15,10 +15,11 @@ ListView { property bool adsFocus: false - onAdsFocusChanged: { - if (!root.adsFocus) - dialog.close() - } + // Temporarily remove due to dockwidget focus issue + //onAdsFocusChanged: { + // if (!root.adsFocus) + // dialog.close() + //} clip: true interactive: true From 1a88cf1446b5020f8840c620dcdda52d6e072d74 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 18 Sep 2023 12:40:47 +0300 Subject: [PATCH 024/130] QmlDesigner: Open 3D view toolbar popups to correct position The button that is shown under extension menu is not actually the same button widget that is tied to the action, so we need to resolve the menu position based on toolbar dimensions in that case. Fixes: QDS-10635 Change-Id: I0c2319041638f9f1cb19f3e57fccbb993e31c743 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../qmldesigner/components/edit3d/edit3dview.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index f05f813929a..a33c9c0019e 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -589,9 +589,17 @@ QPoint Edit3DView::resolveToolbarPopupPos(Edit3DAction *action) const const QList &objects = action->action()->associatedObjects(); for (QObject *obj : objects) { if (auto button = qobject_cast(obj)) { - // Add small negative modifier to Y coordinate, so highlighted toolbar buttons don't - // peek from under the popup - pos = button->mapToGlobal(QPoint(0, -2)); + if (auto toolBar = qobject_cast(button->parent())) { + // If the button has not yet been shown (i.e. it starts under extension menu), + // the button x value will be zero. + if (button->x() >= toolBar->width() - button->width() || button->x() == 0) { + pos = toolBar->mapToGlobal(QPoint(toolBar->width() - button->width(), 4)); + } else { + // Add small negative modifier to Y coordinate, so highlighted toolbar buttons don't + // peek from under the popup + pos = button->mapToGlobal(QPoint(0, -2)); + } + } break; } } From 516a2238e29844ca3dab1186d413dd66ae8b2d85 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 15 Sep 2023 16:06:14 +0300 Subject: [PATCH 025/130] QmlDesigner: Ensure we save the latest values on 3D snap dialog close Track value property of spin boxes changes directly instead of the extra valueModified signal, which is not emitted in all cases. Also removed duplicate applying of snap configuration. Fixes: QDS-10653 Change-Id: If0ce7bffa1dcfb5b01e80b6e962f356f91f44d51 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../SnapConfigurationDialog.qml | 6 ++--- .../components/edit3d/snapconfiguration.cpp | 24 +++++++++---------- .../components/edit3d/snapconfiguration.h | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml index 340570ff9b2..43460b7f719 100644 --- a/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml @@ -132,7 +132,7 @@ Rectangle { ToolTip.text: qsTr("Snap interval for move gizmo.") ToolTip.delay: root.toolTipDelay - onValueModified: posInt = value + onValueChanged: posInt = value } StudioControls.CheckBox { @@ -167,7 +167,7 @@ Rectangle { ToolTip.text: qsTr("Snap interval in degrees for rotation gizmo.") ToolTip.delay: root.toolTipDelay - onValueModified: rotInt = value + onValueChanged: rotInt = value } StudioControls.CheckBox { @@ -202,7 +202,7 @@ Rectangle { ToolTip.text: qsTr("Snap interval for scale gizmo in percentage of original scale.") ToolTip.delay: root.toolTipDelay - onValueModified: scaleInt = value + onValueChanged: scaleInt = value } StudioControls.CheckBox { diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp index 1412588f902..f1d8889364b 100644 --- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp @@ -47,7 +47,8 @@ SnapConfiguration::SnapConfiguration(Edit3DView *view) SnapConfiguration::~SnapConfiguration() { - cleanup(); + delete m_configDialog; + restoreCursor(); } void SnapConfiguration::apply() @@ -67,12 +68,10 @@ void SnapConfiguration::apply() m_rotationInterval); Edit3DViewConfig::save(DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE_INTERVAL, m_scaleInterval); - m_view->syncSnapAuxPropsToSettings(); + if (!m_view.isNull()) + m_view->syncSnapAuxPropsToSettings(); } - - restoreCursor(); - - cancel(); + deleteLater(); } void SnapConfiguration::resetDefaults() @@ -234,9 +233,12 @@ void SnapConfiguration::setScaleInt(double value) } } -void SnapConfiguration::cleanup() +void SnapConfiguration::asyncClose() { - delete m_configDialog; + QTimer::singleShot(0, this, [this]() { + if (!m_configDialog.isNull() && m_configDialog->isVisible()) + m_configDialog->close(); + }); } void SnapConfiguration::cancel() @@ -249,15 +251,13 @@ void SnapConfiguration::cancel() bool SnapConfiguration::eventFilter(QObject *obj, QEvent *event) { - // Closing dialog always applies the changes - if (obj == m_configDialog) { if (event->type() == QEvent::FocusOut) { - apply(); + asyncClose(); } else if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Escape) - apply(); + asyncClose(); } else if (event->type() == QEvent::Close) { apply(); } diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h index b744e63053f..729e6ce7d10 100644 --- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h @@ -90,7 +90,7 @@ protected: bool eventFilter(QObject *obj, QEvent *event) override; private: - void cleanup(); + void asyncClose(); QPointer m_configDialog; QPointer m_view; From 4949c1911edc66c52fa7c471dbf02bf5204c95ca Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Fri, 15 Sep 2023 16:17:37 +0200 Subject: [PATCH 026/130] QmlDesigner: Add code preview indentation Task-number: QDS-10680 Change-Id: I61528f517ed4baef2115fbddf1b014b9f7f966d2 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../ConnectionsDialogForm.qml | 2 +- .../connectioneditor/connectionmodel.cpp | 22 ++++++++++++++++--- .../connectioneditor/connectionmodel.h | 2 ++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 057e08e305b..075c97641a1 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -223,7 +223,7 @@ Column { id: code anchors.fill: parent anchors.margins: 4 - text: backend.source + text: backend.indentedSource color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.myFontSize wrapMode: Text.WordWrap diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index f2ef1ca02cc..f07f61babbf 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -11,18 +11,21 @@ #include #include #include -#include -#include +#include #include #include #include #include +#include +#include #include -#include #include +#include #include +#include +#include #include namespace { @@ -911,6 +914,19 @@ ConditionListModel *ConnectionModelBackendDelegate::conditionListModel() return &m_conditionListModel; } +QString ConnectionModelBackendDelegate::indentedSource() const +{ + if (m_source.isEmpty()) + return {}; + + QTextDocument doc(m_source); + QTextCursor cursor(&doc); + IndentingTextEditModifier mod(&doc, cursor); + + mod.indent(0, m_source.length() - 1); + return mod.text(); +} + QString ConnectionModelBackendDelegate::source() const { return m_source; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index 300fcb061a4..03ee4e7144c 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -266,6 +266,7 @@ class ConnectionModelBackendDelegate : public QObject Q_PROPERTY(bool hasCondition READ hasCondition NOTIFY hasConditionChanged) Q_PROPERTY(bool hasElse READ hasElse NOTIFY hasElseChanged) Q_PROPERTY(QString source READ source NOTIFY sourceChanged) + Q_PROPERTY(QString indentedSource READ indentedSource NOTIFY sourceChanged) Q_PROPERTY(PropertyTreeModel *propertyTreeModel READ propertyTreeModel CONSTANT) Q_PROPERTY(PropertyListProxyModel *propertyListProxyModel READ propertyListProxyModel CONSTANT) @@ -308,6 +309,7 @@ private: ConnectionModelStatementDelegate *okStatement(); ConnectionModelStatementDelegate *koStatement(); ConditionListModel *conditionListModel(); + QString indentedSource() const; QString source() const; void setSource(const QString &source); From 106bb82d73ce1a6bb03354bd557e69def1fd02a1 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Fri, 15 Sep 2023 18:28:39 +0200 Subject: [PATCH 027/130] QmlDesigner: Remove Connections Editor action Task-number: QDS-10606 Change-Id: I0b0d4364e6c80eddee3bb1d8b8491c8c00542593 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../componentcore/designeractionmanager.cpp | 34 ++----------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 228934b2f83..628d49ca074 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -741,19 +741,7 @@ public: activeSignalHandlerGroup->addMenu(editSlotGroup); } - ActionTemplate *openEditorAction = new ActionTemplate( - (propertyName + "OpenEditorId").toLatin1(), - QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Connections Editor")), - [=](const SelectionContext &) { - signalHandler.parentModelNode().view()->executeInTransaction( - "ConnectionsModelNodeActionGroup::" - "openConnectionsEditor", - [signalHandler]() { - ActionEditor::invokeEditor(signalHandler, removeSignal); - }); - }); - - activeSignalHandlerGroup->addAction(openEditorAction); + //add an action to open Connection Form from here: ActionTemplate *removeSignalHandlerAction = new ActionTemplate( (propertyName + "RemoveSignalHandlerId").toLatin1(), @@ -822,25 +810,7 @@ public: } } - ActionTemplate *openEditorAction = new ActionTemplate( - (signalStr + "OpenEditorId").toLatin1(), - QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Connections Editor")), - [=](const SelectionContext &) { - currentNode.view()->executeInTransaction( - "ConnectionsModelNodeActionGroup::" - "openConnectionsEditor", - [=]() { - ModelNode newConnectionNode = createNewConnection(currentNode); - - SignalHandlerProperty newHandler = newConnectionNode.signalHandlerProperty( - prependSignal(signalStr).toLatin1()); - - newHandler.setSource( - QString("console.log(\"%1.%2\")").arg(currentNode.id(), signalStr)); - ActionEditor::invokeEditor(newHandler, removeSignal, true); - }); - }); - newSignal->addAction(openEditorAction); + //add an action to open Connection Form from here addConnection->addMenu(newSignal); } From c693bd06b340e3e705f5c928c180e212d6cc683e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 18 Sep 2023 14:41:20 +0200 Subject: [PATCH 028/130] QmlDesigner: Fix toolbar height for non QDS styles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-10687 Task-number: QDS-10596 Change-Id: Ia4a7e958c6719cafa3a3fe2fea588cbd20c5b703 Reviewed-by: Henning Gründl --- src/plugins/qmldesigner/components/toolbar/toolbar.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp index e4f523c55cc..0190a665f70 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp @@ -65,6 +65,7 @@ Utils::UniqueObjectPtr ToolBar::create() toolBar->setFloatable(false); toolBar->setMovable(false); + toolBar->setProperty("_q_custom_style_disabled", true); auto quickWidget = std::make_unique(); From 6dc42439954549fc29c4685d957d6bf2e06d5ab9 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 15 Sep 2023 19:02:17 +0200 Subject: [PATCH 029/130] QmlDesigner: Choose proper default signal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I2a9493d0cc2a49afd624d58b53df54686d827422 Reviewed-by: Henning Gründl --- .../connectioneditor/connectionmodel.cpp | 105 +++++++++++++----- 1 file changed, 75 insertions(+), 30 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index f07f61babbf..9fca617e359 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -303,6 +303,46 @@ ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connectio return result; } +static QString addOnToSignalName(const QString &signal) +{ + QString ret = signal; + ret[0] = ret.at(0).toUpper(); + ret.prepend("on"); + return ret; +} + +static PropertyName getFirstSignalForTarget(const NodeMetaInfo &target) +{ + PropertyName ret = "clicked"; + + if (!target.isValid()) + return ret; + + const auto signalNames = target.signalNames(); + if (signalNames.isEmpty()) + return ret; + + const PropertyNameList priorityList = {"clicked", + "toggled", + "started", + "stopped", + "moved", + "valueChanged", + "visualPostionChanged", + "accepted", + "currentIndexChanged", + "activeFocusChanged"}; + + for (const auto &signal : priorityList) { + if (signalNames.contains(signal)) + return signal; + } + + ret = target.signalNames().first(); + + return ret; +} + void ConnectionModel::addConnection() { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_CONNECTION_ADDED); @@ -314,33 +354,46 @@ void ConnectionModel::addConnection() NodeMetaInfo nodeMetaInfo = connectionView()->model()->qtQuickConnectionsMetaInfo(); if (nodeMetaInfo.isValid()) { - connectionView()->executeInTransaction("ConnectionModel::addConnection", [=, &rootModelNode](){ - ModelNode newNode = connectionView()->createModelNode("QtQuick.Connections", - nodeMetaInfo.majorVersion(), - nodeMetaInfo.minorVersion()); - QString source = "console.log(\"clicked\")"; + ModelNode selectedNode = connectionView()->selectedModelNodes().constFirst(); + const PropertyName signalHandlerName = addOnToSignalName( + QString::fromUtf8(getFirstSignalForTarget( + selectedNode.metaInfo()))) + .toUtf8(); - if (connectionView()->selectedModelNodes().size() == 1) { - ModelNode selectedNode = connectionView()->selectedModelNodes().constFirst(); - if (QmlItemNode::isValidQmlItemNode(selectedNode)) - selectedNode.nodeAbstractProperty("data").reparentHere(newNode); - else - rootModelNode.nodeAbstractProperty(rootModelNode.metaInfo().defaultPropertyName()).reparentHere(newNode); + connectionView() + ->executeInTransaction("ConnectionModel::addConnection", [=, &rootModelNode]() { + ModelNode newNode = connectionView() + ->createModelNode("QtQuick.Connections", + nodeMetaInfo.majorVersion(), + nodeMetaInfo.minorVersion()); + QString source = "console.log(\"clicked\")"; - if (QmlItemNode(selectedNode).isFlowActionArea() || QmlVisualNode(selectedNode).isFlowTransition()) - source = selectedNode.validId() + ".trigger()"; + if (connectionView()->selectedModelNodes().size() == 1) { + ModelNode selectedNode = connectionView()->selectedModelNodes().constFirst(); + if (QmlItemNode::isValidQmlItemNode(selectedNode)) + selectedNode.nodeAbstractProperty("data").reparentHere(newNode); + else + rootModelNode + .nodeAbstractProperty(rootModelNode.metaInfo().defaultPropertyName()) + .reparentHere(newNode); - if (!connectionView()->selectedModelNodes().constFirst().id().isEmpty()) - newNode.bindingProperty("target").setExpression(selectedNode.validId()); - } else { - rootModelNode.nodeAbstractProperty(rootModelNode.metaInfo().defaultPropertyName()).reparentHere(newNode); - newNode.bindingProperty("target").setExpression(rootModelNode.validId()); - } + if (QmlItemNode(selectedNode).isFlowActionArea() + || QmlVisualNode(selectedNode).isFlowTransition()) + source = selectedNode.validId() + ".trigger()"; - newNode.signalHandlerProperty("onClicked").setSource(source); + if (!connectionView()->selectedModelNodes().constFirst().id().isEmpty()) + newNode.bindingProperty("target").setExpression(selectedNode.validId()); + } else { + rootModelNode + .nodeAbstractProperty(rootModelNode.metaInfo().defaultPropertyName()) + .reparentHere(newNode); + newNode.bindingProperty("target").setExpression(rootModelNode.validId()); + } - selectProperty(newNode.signalHandlerProperty("onClicked")); - }); + newNode.signalHandlerProperty(signalHandlerName).setSource(source); + + selectProperty(newNode.signalHandlerProperty(signalHandlerName)); + }); } } } @@ -790,14 +843,6 @@ QString removeOnFromSignalName(const QString &signal) return ret; } -QString addOnToSignalName(const QString &signal) -{ - QString ret = signal; - ret[0] = ret.at(0).toUpper(); - ret.prepend("on"); - return ret; -} - void ConnectionModelBackendDelegate::setCurrentRow(int i) { if (m_currentRow == i) From a6e8a247b3e49cce4e830d839925d3d985b89344 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 18 Sep 2023 13:32:55 +0300 Subject: [PATCH 030/130] QmlDesigner: Fix minimum value for NumberAnimation to and from fields Change-Id: Ic5fcb1d69ab0c3369dacbc93da71714b20253e25 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- .../QtQuick/NumberAnimationSpecifics.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/NumberAnimationSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/NumberAnimationSpecifics.qml index 390795ff795..f370391b17e 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/NumberAnimationSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/NumberAnimationSpecifics.qml @@ -27,7 +27,7 @@ Column { implicitWidth: StudioTheme.Values.twoControlColumnWidth + StudioTheme.Values.actionIndicatorWidth maximumValue: 9999999 - minimumValue: -1 + minimumValue: -9999999 backendValue: backendValues.from } @@ -44,7 +44,7 @@ Column { implicitWidth: StudioTheme.Values.twoControlColumnWidth + StudioTheme.Values.actionIndicatorWidth maximumValue: 9999999 - minimumValue: -1 + minimumValue: -9999999 backendValue: backendValues.to } From 7b183a774c981c760c2e6a8e2a8b2f6f05dee633 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Fri, 15 Sep 2023 15:36:15 +0300 Subject: [PATCH 031/130] QmlDesigner: Move effect maker to an independent plugin Task-number: QDS-10656 Change-Id: I1c1e67d3461650bfaec46ccc10b832effce76ad9 Reviewed-by: Mahmoud Badri --- src/plugins/CMakeLists.txt | 4 + src/plugins/effectmakernew/CMakeLists.txt | 27 ++++++ .../effectmakernew/EffectMakerNew.json.in | 15 ++++ .../compositionnode.cpp | 5 +- .../compositionnode.h | 5 +- .../effectmakercontextobject.cpp | 11 +-- .../effectmakercontextobject.h | 7 +- .../effectmakermodel.cpp | 5 +- .../effectmakermodel.h | 5 +- .../effectmakernodesmodel.cpp | 5 +- .../effectmakernodesmodel.h | 5 +- .../effectmakernew/effectmakerplugin.cpp | 46 ++++++++++ .../effectmakernew/effectmakerplugin.h | 32 +++++++ .../effectmakeruniformsmodel.cpp | 5 +- .../effectmakeruniformsmodel.h | 5 +- .../effectmakernew/effectmakerview.cpp | 83 +++++++++++++++++++ src/plugins/effectmakernew/effectmakerview.h | 46 ++++++++++ .../effectmakerwidget.cpp | 16 ++-- .../effectmakerwidget.h | 9 +- .../effectnode.cpp | 5 +- .../effectnode.h | 5 +- .../effectnodescategory.cpp | 5 +- .../effectnodescategory.h | 5 +- .../effectutils.cpp | 5 +- .../effectutils.h | 5 +- .../shaderfeatures.cpp | 5 +- .../shaderfeatures.h | 5 +- .../syntaxhighlighterdata.cpp | 5 +- .../syntaxhighlighterdata.h | 5 +- .../uniform.cpp | 8 +- .../effectmaker => effectmakernew}/uniform.h | 10 ++- src/plugins/qmldesigner/CMakeLists.txt | 18 ---- .../components/componentcore/viewmanager.cpp | 9 -- .../effectmaker/effectmakerview.cpp | 69 --------------- .../components/effectmaker/effectmakerview.h | 34 -------- .../propertyeditor/propertyeditorvalue.h | 2 +- .../propertyeditor/qmlmodelnodeproxy.h | 2 +- src/plugins/qmldesigner/designmodecontext.cpp | 13 --- src/plugins/qmldesigner/designmodecontext.h | 9 -- 39 files changed, 343 insertions(+), 217 deletions(-) create mode 100644 src/plugins/effectmakernew/CMakeLists.txt create mode 100644 src/plugins/effectmakernew/EffectMakerNew.json.in rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/compositionnode.cpp (98%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/compositionnode.h (96%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectmakercontextobject.cpp (93%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectmakercontextobject.h (97%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectmakermodel.cpp (99%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectmakermodel.h (98%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectmakernodesmodel.cpp (98%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectmakernodesmodel.h (94%) create mode 100644 src/plugins/effectmakernew/effectmakerplugin.cpp create mode 100644 src/plugins/effectmakernew/effectmakerplugin.h rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectmakeruniformsmodel.cpp (97%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectmakeruniformsmodel.h (95%) create mode 100644 src/plugins/effectmakernew/effectmakerview.cpp create mode 100644 src/plugins/effectmakernew/effectmakerview.h rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectmakerwidget.cpp (88%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectmakerwidget.h (88%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectnode.cpp (94%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectnode.h (93%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectnodescategory.cpp (90%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectnodescategory.h (92%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectutils.cpp (90%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/effectutils.h (86%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/shaderfeatures.cpp (97%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/shaderfeatures.h (94%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/syntaxhighlighterdata.cpp (98%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/syntaxhighlighterdata.h (88%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/uniform.cpp (98%) rename src/plugins/{qmldesigner/components/effectmaker => effectmakernew}/uniform.h (93%) delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakerview.h diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 4fd8b655730..68985fbf000 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -112,3 +112,7 @@ add_subdirectory(mcusupport) add_subdirectory(saferenderer) add_subdirectory(copilot) add_subdirectory(terminal) + +if (WITH_QMLDESIGNER) + add_subdirectory(effectmakernew) +endif() diff --git a/src/plugins/effectmakernew/CMakeLists.txt b/src/plugins/effectmakernew/CMakeLists.txt new file mode 100644 index 00000000000..09d70a7fea0 --- /dev/null +++ b/src/plugins/effectmakernew/CMakeLists.txt @@ -0,0 +1,27 @@ +find_package(Qt6 OPTIONAL_COMPONENTS Gui Quick ShaderTools) + +add_qtc_plugin(EffectMakerNew + CONDITION TARGET QmlDesigner AND TARGET Qt::ShaderTools + PLUGIN_DEPENDS + QtCreator::Core QtCreator::QmlDesigner + DEPENDS + Qt::Core + QtCreator::Utils Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick Qt::ShaderTools + SOURCES + effectmakerplugin.cpp effectmakerplugin.h + effectmakerwidget.cpp effectmakerwidget.h + effectmakerview.cpp effectmakerview.h + effectmakermodel.cpp effectmakermodel.h + effectmakernodesmodel.cpp effectmakernodesmodel.h + effectmakeruniformsmodel.cpp effectmakeruniformsmodel.h + effectnode.cpp effectnode.h + effectnodescategory.cpp effectnodescategory.h + compositionnode.cpp compositionnode.h + uniform.cpp uniform.h + effectutils.cpp effectutils.h + effectmakercontextobject.cpp effectmakercontextobject.h + shaderfeatures.cpp shaderfeatures.h + syntaxhighlighterdata.cpp syntaxhighlighterdata.h + + BUILD_DEFAULT OFF +) diff --git a/src/plugins/effectmakernew/EffectMakerNew.json.in b/src/plugins/effectmakernew/EffectMakerNew.json.in new file mode 100644 index 00000000000..46c5e12247f --- /dev/null +++ b/src/plugins/effectmakernew/EffectMakerNew.json.in @@ -0,0 +1,15 @@ +{ + \"Name\" : \"EffectMakerNew\", + \"Version\" : \"$$QTCREATOR_VERSION\", + \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", + \"Revision\" : \"$$QTC_PLUGIN_REVISION\", + \"Vendor\" : \"The Qt Company Ltd\", + \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", + \"License\" : [ \"Commercial Usage\", + \"\", + \"Licensees holding valid Qt Enterprise licenses may use this plugin in accordance with the Qt Enterprise License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\" + ], + \"Description\" : \"Plugin for Effect Maker.\", + \"Url\" : \"http://www.qt.io\", + $$dependencyList +} diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp b/src/plugins/effectmakernew/compositionnode.cpp similarity index 98% rename from src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp rename to src/plugins/effectmakernew/compositionnode.cpp index 2e707636098..74e43c76d56 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp +++ b/src/plugins/effectmakernew/compositionnode.cpp @@ -12,7 +12,7 @@ #include #include -namespace QmlDesigner { +namespace EffectMaker { CompositionNode::CompositionNode(const QString &qenPath) { @@ -119,4 +119,5 @@ void CompositionNode::parse(const QString &qenPath) } } -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h b/src/plugins/effectmakernew/compositionnode.h similarity index 96% rename from src/plugins/qmldesigner/components/effectmaker/compositionnode.h rename to src/plugins/effectmakernew/compositionnode.h index 840abde96ea..2637bfb3879 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h +++ b/src/plugins/effectmakernew/compositionnode.h @@ -7,7 +7,7 @@ #include -namespace QmlDesigner { +namespace EffectMaker { class CompositionNode : public QObject { @@ -57,4 +57,5 @@ private: EffectMakerUniformsModel m_unifomrsModel; }; -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.cpp b/src/plugins/effectmakernew/effectmakercontextobject.cpp similarity index 93% rename from src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.cpp rename to src/plugins/effectmakernew/effectmakercontextobject.cpp index 7ee8399c2cf..6a37765d5c2 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.cpp +++ b/src/plugins/effectmakernew/effectmakercontextobject.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include @@ -27,7 +27,7 @@ #include -namespace QmlDesigner { +namespace EffectMaker { EffectMakerContextObject::EffectMakerContextObject(QQmlContext *context, QObject *parent) : QObject(parent) @@ -117,7 +117,7 @@ void EffectMakerContextObject::setBackendValues(QQmlPropertyMap *newBackendValue emit backendValuesChanged(); } -void EffectMakerContextObject::setModel(Model *model) +void EffectMakerContextObject::setModel(QmlDesigner::Model *model) { m_model = model; } @@ -169,7 +169,7 @@ int EffectMakerContextObject::devicePixelRatio() QStringList EffectMakerContextObject::allStatesForId(const QString &id) { if (m_model && m_model->rewriterView()) { - const QmlObjectNode node = m_model->rewriterView()->modelNodeForId(id); + const QmlDesigner::QmlObjectNode node = m_model->rewriterView()->modelNodeForId(id); if (node.isValid()) return node.allStateNames(); } @@ -182,4 +182,5 @@ bool EffectMakerContextObject::isBlocked(const QString &) const return false; } -} // QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.h b/src/plugins/effectmakernew/effectmakercontextobject.h similarity index 97% rename from src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.h rename to src/plugins/effectmakernew/effectmakercontextobject.h index e27957f4ece..9b76dc36d8f 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.h +++ b/src/plugins/effectmakernew/effectmakercontextobject.h @@ -14,7 +14,7 @@ #include #include -namespace QmlDesigner { +namespace EffectMaker { class EffectMakerContextObject : public QObject { @@ -90,7 +90,7 @@ private: int m_majorVersion = 1; QQmlPropertyMap *m_backendValues = nullptr; - Model *m_model = nullptr; + QmlDesigner::Model *m_model = nullptr; QPoint m_lastPos; @@ -98,4 +98,5 @@ private: bool m_selectionChanged = false; }; -} // QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp similarity index 99% rename from src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp rename to src/plugins/effectmakernew/effectmakermodel.cpp index 42f0977f7dc..438fb8e4201 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -12,7 +12,7 @@ #include -namespace QmlDesigner { +namespace EffectMaker { EffectMakerModel::EffectMakerModel(QObject *parent) : QAbstractListModel{parent} @@ -760,4 +760,5 @@ void EffectMakerModel::setShadersUpToDate(bool UpToDate) emit shadersUpToDateChanged(); } -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h similarity index 98% rename from src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h rename to src/plugins/effectmakernew/effectmakermodel.h index 58c6a93fd8f..08441bfb434 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -9,7 +9,7 @@ #include #include -namespace QmlDesigner { +namespace EffectMaker { class CompositionNode; class Uniform; @@ -135,4 +135,5 @@ private: const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); }; -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp b/src/plugins/effectmakernew/effectmakernodesmodel.cpp similarity index 98% rename from src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp rename to src/plugins/effectmakernew/effectmakernodesmodel.cpp index 521e3e7ce21..3b894d91575 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp +++ b/src/plugins/effectmakernew/effectmakernodesmodel.cpp @@ -7,7 +7,7 @@ #include -namespace QmlDesigner { +namespace EffectMaker { EffectMakerNodesModel::EffectMakerNodesModel(QObject *parent) : QAbstractListModel{parent} @@ -104,4 +104,5 @@ void EffectMakerNodesModel::resetModel() endResetModel(); } -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h b/src/plugins/effectmakernew/effectmakernodesmodel.h similarity index 94% rename from src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h rename to src/plugins/effectmakernew/effectmakernodesmodel.h index 5ed702f84be..28a4e8484f0 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h +++ b/src/plugins/effectmakernew/effectmakernodesmodel.h @@ -9,7 +9,7 @@ #include -namespace QmlDesigner { +namespace EffectMaker { class EffectMakerNodesModel : public QAbstractListModel { @@ -40,4 +40,5 @@ private: bool m_probeNodesDir = false; }; -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakerplugin.cpp b/src/plugins/effectmakernew/effectmakerplugin.cpp new file mode 100644 index 00000000000..3890d3c9d65 --- /dev/null +++ b/src/plugins/effectmakernew/effectmakerplugin.cpp @@ -0,0 +1,46 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectmakerplugin.h" + +#include "effectmakerview.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace EffectMaker { + +bool EffectMakerPlugin::delayedInitialize() +{ + if (m_delayedInitialized) + return true; + + auto *designerPlugin = QmlDesigner::QmlDesignerPlugin::instance(); + auto &viewManager = designerPlugin->viewManager(); + viewManager.registerView(std::make_unique( + QmlDesigner::QmlDesignerPlugin::externalDependenciesForPluginInitializationOnly())); + + m_delayedInitialized = true; + + return true; +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakerplugin.h b/src/plugins/effectmakernew/effectmakerplugin.h new file mode 100644 index 00000000000..116115629e5 --- /dev/null +++ b/src/plugins/effectmakernew/effectmakerplugin.h @@ -0,0 +1,32 @@ +// 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 +#include + +namespace Core { +class ActionContainer; +class ExternalTool; +} + +namespace EffectMaker { + +class EffectMakerPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "EffectMakerNew.json") + +public: + EffectMakerPlugin() {} + ~EffectMakerPlugin() override {} + + bool delayedInitialize() override; + +private: + bool m_delayedInitialized = false; +}; + +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp b/src/plugins/effectmakernew/effectmakeruniformsmodel.cpp similarity index 97% rename from src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp rename to src/plugins/effectmakernew/effectmakeruniformsmodel.cpp index dac01905b67..9313b986404 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp +++ b/src/plugins/effectmakernew/effectmakeruniformsmodel.cpp @@ -7,7 +7,7 @@ #include -namespace QmlDesigner { +namespace EffectMaker { EffectMakerUniformsModel::EffectMakerUniformsModel(QObject *parent) : QAbstractListModel{parent} @@ -72,4 +72,5 @@ QList EffectMakerUniformsModel::uniforms() const return m_uniforms; } -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h b/src/plugins/effectmakernew/effectmakeruniformsmodel.h similarity index 95% rename from src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h rename to src/plugins/effectmakernew/effectmakeruniformsmodel.h index 1d69d6d1b27..9b9651a8720 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h +++ b/src/plugins/effectmakernew/effectmakeruniformsmodel.h @@ -5,7 +5,7 @@ #include -namespace QmlDesigner { +namespace EffectMaker { class Uniform; @@ -42,4 +42,5 @@ private: QList m_uniforms; }; -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakerview.cpp b/src/plugins/effectmakernew/effectmakerview.cpp new file mode 100644 index 00000000000..4bb68f358a4 --- /dev/null +++ b/src/plugins/effectmakernew/effectmakerview.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectmakerview.h" + +#include "effectmakerwidget.h" +#include "effectmakernodesmodel.h" + +#include "nodeinstanceview.h" +#include "qmldesignerconstants.h" + +#include + +#include +#include +#include +#include +#include + +namespace EffectMaker { + +EffectMakerContext::EffectMakerContext(QWidget *widget) + : IContext(widget) +{ + setWidget(widget); + setContext(Core::Context(QmlDesigner::Constants::C_QMLEFFECTMAKER, + QmlDesigner::Constants::C_QT_QUICK_TOOLS_MENU)); +} + +void EffectMakerContext::contextHelp(const HelpCallback &callback) const +{ + qobject_cast(m_widget)->contextHelp(callback); +} + +EffectMakerView::EffectMakerView(QmlDesigner::ExternalDependenciesInterface &externalDependencies) + : AbstractView{externalDependencies} +{ +} + +EffectMakerView::~EffectMakerView() +{} + +bool EffectMakerView::hasWidget() const +{ + return true; +} + +QmlDesigner::WidgetInfo EffectMakerView::widgetInfo() +{ + if (m_widget.isNull()) { + m_widget = new EffectMakerWidget{this}; + + auto context = new EffectMakerContext(m_widget.data()); + Core::ICore::addContextObject(context); + } + + return createWidgetInfo(m_widget.data(), "Effect Maker", + QmlDesigner::WidgetInfo::LeftPane, 0, tr("Effect Maker")); +} + +void EffectMakerView::customNotification(const AbstractView * /*view*/, + const QString & /*identifier*/, + const QList & /*nodeList*/, + const QList & /*data*/) +{ + // TODO +} + +void EffectMakerView::modelAttached(QmlDesigner::Model *model) +{ + AbstractView::modelAttached(model); + + m_widget->effectMakerNodesModel()->loadModel(); + m_widget->initView(); +} + +void EffectMakerView::modelAboutToBeDetached(QmlDesigner::Model *model) +{ + AbstractView::modelAboutToBeDetached(model); +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakerview.h b/src/plugins/effectmakernew/effectmakerview.h new file mode 100644 index 00000000000..2bed1cfc103 --- /dev/null +++ b/src/plugins/effectmakernew/effectmakerview.h @@ -0,0 +1,46 @@ +// 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 "abstractview.h" + +#include + +#include + +namespace EffectMaker { + +class EffectMakerWidget; + +class EffectMakerContext : public Core::IContext +{ + Q_OBJECT + +public: + EffectMakerContext(QWidget *widget); + void contextHelp(const Core::IContext::HelpCallback &callback) const override; +}; + +class EffectMakerView : public QmlDesigner::AbstractView +{ +public: + EffectMakerView(QmlDesigner::ExternalDependenciesInterface &externalDependencies); + ~EffectMakerView() override; + + bool hasWidget() const override; + QmlDesigner::WidgetInfo widgetInfo() override; + + // AbstractView + void modelAttached(QmlDesigner::Model *model) override; + void modelAboutToBeDetached(QmlDesigner::Model *model) override; + +private: + void customNotification(const AbstractView *view, const QString &identifier, + const QList &nodeList, const QList &data) override; + + QPointer m_widget; +}; + +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp b/src/plugins/effectmakernew/effectmakerwidget.cpp similarity index 88% rename from src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp rename to src/plugins/effectmakernew/effectmakerwidget.cpp index f6f96bc886c..72d4c1b2cd6 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp +++ b/src/plugins/effectmakernew/effectmakerwidget.cpp @@ -23,7 +23,7 @@ #include #include -namespace QmlDesigner { +namespace EffectMaker { static QString propertyEditorResourcesPath() { @@ -46,21 +46,22 @@ EffectMakerWidget::EffectMakerWidget(EffectMakerView *view) m_quickWidget->quickWidget()->installEventFilter(this); // create the inner widget - m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_EFFECT_MAKER); + m_quickWidget->quickWidget()->setObjectName(QmlDesigner::Constants::OBJECT_NAME_EFFECT_MAKER); m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); - Theme::setupTheme(m_quickWidget->engine()); + QmlDesigner::Theme::setupTheme(m_quickWidget->engine()); m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); - m_quickWidget->setClearColor(Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); + m_quickWidget->setClearColor(QmlDesigner::Theme::getColor( + QmlDesigner::Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); auto layout = new QHBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); layout->addWidget(m_quickWidget.data()); - setStyleSheet(Theme::replaceCssColors( + setStyleSheet(QmlDesigner::Theme::replaceCssColors( QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); - QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_EFFECTMAKER_TIME); + QmlDesigner::QmlDesignerPlugin::trackWidgetFocusTime(this, QmlDesigner::Constants::EVENT_EFFECTMAKER_TIME); auto map = m_quickWidget->registerPropertyMap("EffectMakerBackend"); map->setProperties({{"effectMakerNodesModel", QVariant::fromValue(m_effectMakerNodesModel.data())}, @@ -142,4 +143,5 @@ void EffectMakerWidget::reloadQmlSource() m_quickWidget->setSource(QUrl::fromLocalFile(effectMakerQmlPath)); } -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h b/src/plugins/effectmakernew/effectmakerwidget.h similarity index 88% rename from src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h rename to src/plugins/effectmakernew/effectmakerwidget.h index d59318eb459..6f55cbc786e 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h +++ b/src/plugins/effectmakernew/effectmakerwidget.h @@ -3,7 +3,7 @@ #pragma once -#include "qmlmodelnodeproxy.h" +#include "qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h" #include @@ -11,7 +11,7 @@ class StudioQuickWidget; -namespace QmlDesigner { +namespace EffectMaker { class EffectMakerView; class EffectMakerModel; @@ -53,7 +53,8 @@ private: QPointer m_effectMakerNodesModel; QPointer m_effectMakerView; QPointer m_quickWidget; - QmlModelNodeProxy m_backendModelNode; + QmlDesigner::QmlModelNodeProxy m_backendModelNode; }; -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp b/src/plugins/effectmakernew/effectnode.cpp similarity index 94% rename from src/plugins/qmldesigner/components/effectmaker/effectnode.cpp rename to src/plugins/effectmakernew/effectnode.cpp index 08d11925f56..292c04d13e5 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp +++ b/src/plugins/effectmakernew/effectnode.cpp @@ -6,7 +6,7 @@ #include #include -namespace QmlDesigner { +namespace EffectMaker { EffectNode::EffectNode(const QString &qenPath) : m_qenPath(qenPath) @@ -39,4 +39,5 @@ QString EffectNode::qenPath() const return m_qenPath; } -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.h b/src/plugins/effectmakernew/effectnode.h similarity index 93% rename from src/plugins/qmldesigner/components/effectmaker/effectnode.h rename to src/plugins/effectmakernew/effectnode.h index 823fe092db0..5c457e2a6de 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectnode.h +++ b/src/plugins/effectmakernew/effectnode.h @@ -6,7 +6,7 @@ #include #include -namespace QmlDesigner { +namespace EffectMaker { class EffectNode : public QObject { @@ -31,4 +31,5 @@ private: QUrl m_iconPath; }; -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.cpp b/src/plugins/effectmakernew/effectnodescategory.cpp similarity index 90% rename from src/plugins/qmldesigner/components/effectmaker/effectnodescategory.cpp rename to src/plugins/effectmakernew/effectnodescategory.cpp index 36a8f0a0d06..7f89766cca1 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.cpp +++ b/src/plugins/effectmakernew/effectnodescategory.cpp @@ -3,7 +3,7 @@ #include "effectnodescategory.h" -namespace QmlDesigner { +namespace EffectMaker { EffectNodesCategory::EffectNodesCategory(const QString &name, const QList &nodes) : m_name(name), @@ -19,4 +19,5 @@ QList EffectNodesCategory::nodes() const return m_categoryNodes; } -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.h b/src/plugins/effectmakernew/effectnodescategory.h similarity index 92% rename from src/plugins/qmldesigner/components/effectmaker/effectnodescategory.h rename to src/plugins/effectmakernew/effectnodescategory.h index ba7d6868bc6..25f5d8d4bd6 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.h +++ b/src/plugins/effectmakernew/effectnodescategory.h @@ -7,7 +7,7 @@ #include -namespace QmlDesigner { +namespace EffectMaker { class EffectNodesCategory : public QObject { @@ -27,4 +27,5 @@ private: QList m_categoryNodes; }; -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectutils.cpp b/src/plugins/effectmakernew/effectutils.cpp similarity index 90% rename from src/plugins/qmldesigner/components/effectmaker/effectutils.cpp rename to src/plugins/effectmakernew/effectutils.cpp index 8f45b9a1370..8e2bb625431 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectutils.cpp +++ b/src/plugins/effectmakernew/effectutils.cpp @@ -5,7 +5,7 @@ #include -namespace QmlDesigner { +namespace EffectMaker { QString EffectUtils::codeFromJsonArray(const QJsonArray &codeArray) { @@ -20,4 +20,5 @@ QString EffectUtils::codeFromJsonArray(const QJsonArray &codeArray) return codeString; } -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectutils.h b/src/plugins/effectmakernew/effectutils.h similarity index 86% rename from src/plugins/qmldesigner/components/effectmaker/effectutils.h rename to src/plugins/effectmakernew/effectutils.h index 0abe4d64e6b..e3de9312dce 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectutils.h +++ b/src/plugins/effectmakernew/effectutils.h @@ -7,7 +7,7 @@ QT_FORWARD_DECLARE_CLASS(QJsonArray) -namespace QmlDesigner { +namespace EffectMaker { class EffectUtils { @@ -17,4 +17,5 @@ public: static QString codeFromJsonArray(const QJsonArray &codeArray); }; -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp b/src/plugins/effectmakernew/shaderfeatures.cpp similarity index 97% rename from src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp rename to src/plugins/effectmakernew/shaderfeatures.cpp index 755b203d23c..9ab7a282328 100644 --- a/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp +++ b/src/plugins/effectmakernew/shaderfeatures.cpp @@ -5,7 +5,7 @@ #include #include -namespace QmlDesigner { +namespace EffectMaker { ShaderFeatures::ShaderFeatures() { @@ -77,4 +77,5 @@ void ShaderFeatures::checkLine(const QString &line, Features &features) features.setFlag(BlurSources, true); } -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h b/src/plugins/effectmakernew/shaderfeatures.h similarity index 94% rename from src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h rename to src/plugins/effectmakernew/shaderfeatures.h index 35fb507066d..c0d3b72b847 100644 --- a/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h +++ b/src/plugins/effectmakernew/shaderfeatures.h @@ -6,7 +6,7 @@ #include #include -namespace QmlDesigner { +namespace EffectMaker { class ShaderFeatures { @@ -36,4 +36,5 @@ private: }; Q_DECLARE_OPERATORS_FOR_FLAGS(ShaderFeatures::Features) -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.cpp b/src/plugins/effectmakernew/syntaxhighlighterdata.cpp similarity index 98% rename from src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.cpp rename to src/plugins/effectmakernew/syntaxhighlighterdata.cpp index 47020ed0b0f..4a6face8196 100644 --- a/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.cpp +++ b/src/plugins/effectmakernew/syntaxhighlighterdata.cpp @@ -3,7 +3,7 @@ #include "syntaxhighlighterdata.h" -namespace QmlDesigner { +namespace EffectMaker { static constexpr QByteArrayView shader_arg_names[] { { "gl_Position" }, @@ -186,5 +186,6 @@ QList SyntaxHighlighterData::reservedFunctionNames() return { std::begin(shader_function_names), std::end(shader_function_names) }; } -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.h b/src/plugins/effectmakernew/syntaxhighlighterdata.h similarity index 88% rename from src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.h rename to src/plugins/effectmakernew/syntaxhighlighterdata.h index 6342ea094aa..bce1100e05c 100644 --- a/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.h +++ b/src/plugins/effectmakernew/syntaxhighlighterdata.h @@ -6,7 +6,7 @@ #include #include -namespace QmlDesigner { +namespace EffectMaker { class SyntaxHighlighterData { @@ -18,5 +18,6 @@ public: static QList reservedFunctionNames(); }; -} // namespace QmlDesigner +} // namespace EffectMaker + diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp b/src/plugins/effectmakernew/uniform.cpp similarity index 98% rename from src/plugins/qmldesigner/components/effectmaker/uniform.cpp rename to src/plugins/effectmakernew/uniform.cpp index 8074c3cc95a..5cad43e0c95 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp +++ b/src/plugins/effectmakernew/uniform.cpp @@ -2,14 +2,14 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "uniform.h" +#include -#include "propertyeditorvalue.h" #include #include #include -namespace QmlDesigner { +namespace EffectMaker { Uniform::Uniform(const QJsonObject &propObj) { @@ -45,7 +45,7 @@ Uniform::Uniform(const QJsonObject &propObj) setValueData(value, defaultValue, minValue, maxValue); - m_backendValue = new PropertyEditorValue(this); + m_backendValue = new QmlDesigner::PropertyEditorValue(this); m_backendValue->setValue(value); } @@ -321,4 +321,4 @@ QString Uniform::typeToProperty(Uniform::Type type) return QString(); } -} // namespace QmlDesigner +} // namespace EffectMaker diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/effectmakernew/uniform.h similarity index 93% rename from src/plugins/qmldesigner/components/effectmaker/uniform.h rename to src/plugins/effectmakernew/uniform.h index 67699c5e53a..7bad706cb3e 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/effectmakernew/uniform.h @@ -6,13 +6,15 @@ #include #include +#include + QT_FORWARD_DECLARE_CLASS(QColor) QT_FORWARD_DECLARE_CLASS(QJsonObject) QT_FORWARD_DECLARE_CLASS(QVector2D) -namespace QmlDesigner { +namespace EffectMaker { + -class PropertyEditorValue; class Uniform : public QObject { @@ -97,7 +99,7 @@ private: bool m_useCustomValue = false; bool m_enabled = true; bool m_enableMipmap = false; - PropertyEditorValue *m_backendValue = nullptr; + QmlDesigner::PropertyEditorValue *m_backendValue = nullptr; bool operator==(const Uniform &rhs) const noexcept { @@ -105,4 +107,4 @@ private: } }; -} // namespace QmlDesigner +} // namespace EffectMaker diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 5d9263771c2..dfa4f63b8b5 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -709,24 +709,6 @@ extend_qtc_plugin(QmlDesigner assetslibraryiconprovider.cpp assetslibraryiconprovider.h ) -extend_qtc_plugin(QmlDesigner - SOURCES_PREFIX components/effectmaker - SOURCES - effectmakerwidget.cpp effectmakerwidget.h - effectmakerview.cpp effectmakerview.h - effectmakermodel.cpp effectmakermodel.h - effectmakernodesmodel.cpp effectmakernodesmodel.h - effectmakeruniformsmodel.cpp effectmakeruniformsmodel.h - effectnode.cpp effectnode.h - effectnodescategory.cpp effectnodescategory.h - compositionnode.cpp compositionnode.h - uniform.cpp uniform.h - effectutils.cpp effectutils.h - effectmakercontextobject.cpp effectmakercontextobject.h - shaderfeatures.cpp shaderfeatures.h - syntaxhighlighterdata.cpp syntaxhighlighterdata.h -) - extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/navigator SOURCES diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index 8c06085393c..26f9a974211 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -56,7 +55,6 @@ public: , contentLibraryView{externalDependencies} , componentView{externalDependencies} , edit3DView{externalDependencies} - , effectMakerView{externalDependencies} , formEditorView{externalDependencies} , textEditorView{externalDependencies} , assetsLibraryView{externalDependencies} @@ -79,7 +77,6 @@ public: ContentLibraryView contentLibraryView; ComponentView componentView; Edit3DView edit3DView; - EffectMakerView effectMakerView; FormEditorView formEditorView; TextEditorView textEditorView; AssetsLibraryView assetsLibraryView; @@ -212,9 +209,6 @@ QList ViewManager::standardViews() const .toBool()) list.append(&d->debugView); - if (qEnvironmentVariableIsSet("ENABLE_QDS_EFFECTMAKER")) - list.append(&d->effectMakerView); - if (qEnvironmentVariableIsSet("ENABLE_QDS_COLLECTIONVIEW")) list.append(&d->collectionView); @@ -393,9 +387,6 @@ QList ViewManager::widgetInfos() const widgetInfoList.append(d->textureEditorView.widgetInfo()); widgetInfoList.append(d->statesEditorView.widgetInfo()); - if (qEnvironmentVariableIsSet("ENABLE_QDS_EFFECTMAKER")) - widgetInfoList.append(d->effectMakerView.widgetInfo()); - if (qEnvironmentVariableIsSet("ENABLE_QDS_COLLECTIONVIEW")) widgetInfoList.append(d->collectionView.widgetInfo()); diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp deleted file mode 100644 index 641b41a8ce2..00000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "effectmakerview.h" - -#include "effectmakerwidget.h" -#include "effectmakernodesmodel.h" -#include "designmodecontext.h" -#include "nodeinstanceview.h" - -#include - -#include -#include -#include -#include -#include - -namespace QmlDesigner { - -EffectMakerView::EffectMakerView(ExternalDependenciesInterface &externalDependencies) - : AbstractView{externalDependencies} -{ -} - -EffectMakerView::~EffectMakerView() -{} - -bool EffectMakerView::hasWidget() const -{ - return true; -} - -WidgetInfo EffectMakerView::widgetInfo() -{ - if (m_widget.isNull()) { - m_widget = new EffectMakerWidget{this}; - - auto context = new Internal::EffectMakerContext(m_widget.data()); - Core::ICore::addContextObject(context); - } - - return createWidgetInfo(m_widget.data(), "Effect Maker", WidgetInfo::LeftPane, 0, tr("Effect Maker")); -} - -void EffectMakerView::customNotification(const AbstractView * /*view*/, - const QString & /*identifier*/, - const QList & /*nodeList*/, - const QList & /*data*/) -{ - // TODO -} - -void EffectMakerView::modelAttached(Model *model) -{ - AbstractView::modelAttached(model); - - // Add some dummy effects data - //m_widget->effectMakerModel()->setEffects({"Drop Shadow", "Colorize", "Fast Blue"}); // TODO - m_widget->effectMakerNodesModel()->loadModel(); - m_widget->initView(); -} - -void EffectMakerView::modelAboutToBeDetached(Model *model) -{ - AbstractView::modelAboutToBeDetached(model); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.h b/src/plugins/qmldesigner/components/effectmaker/effectmakerview.h deleted file mode 100644 index 53e58acc67d..00000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.h +++ /dev/null @@ -1,34 +0,0 @@ -// 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 "abstractview.h" - -#include - -namespace QmlDesigner { - -class EffectMakerWidget; - -class EffectMakerView : public AbstractView -{ -public: - EffectMakerView(ExternalDependenciesInterface &externalDependencies); - ~EffectMakerView() override; - - bool hasWidget() const override; - WidgetInfo widgetInfo() override; - - // AbstractView - void modelAttached(Model *model) override; - void modelAboutToBeDetached(Model *model) override; - -private: - void customNotification(const AbstractView *view, const QString &identifier, - const QList &nodeList, const QList &data) override; - - QPointer m_widget; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h index 9a4bbd280e9..59236c4fe21 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h @@ -51,7 +51,7 @@ private: PropertyEditorValue *m_editorValue = nullptr; }; -class PropertyEditorValue : public QObject +class QMLDESIGNERCORE_EXPORT PropertyEditorValue : public QObject { Q_OBJECT diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h index baee63efc1c..4740b01fbda 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h @@ -9,7 +9,7 @@ namespace QmlDesigner { -class QmlModelNodeProxy : public QObject +class QMLDESIGNERCORE_EXPORT QmlModelNodeProxy : public QObject { Q_OBJECT diff --git a/src/plugins/qmldesigner/designmodecontext.cpp b/src/plugins/qmldesigner/designmodecontext.cpp index 13a03bc10b1..43fb0d9d756 100644 --- a/src/plugins/qmldesigner/designmodecontext.cpp +++ b/src/plugins/qmldesigner/designmodecontext.cpp @@ -6,7 +6,6 @@ #include "collectionwidget.h" #include "designmodewidget.h" #include "edit3dwidget.h" -#include "effectmakerwidget.h" #include "formeditorwidget.h" #include "materialbrowserwidget.h" #include "navigatorwidget.h" @@ -99,18 +98,6 @@ void TextEditorContext::contextHelp(const HelpCallback &callback) const qobject_cast(m_widget)->contextHelp(callback); } -EffectMakerContext::EffectMakerContext(QWidget *widget) - : IContext(widget) -{ - setWidget(widget); - setContext(Core::Context(Constants::C_QMLEFFECTMAKER, Constants::C_QT_QUICK_TOOLS_MENU)); -} - -void EffectMakerContext::contextHelp(const HelpCallback &callback) const -{ - qobject_cast(m_widget)->contextHelp(callback); -} - CollectionEditorContext::CollectionEditorContext(QWidget *widget) : IContext(widget) { diff --git a/src/plugins/qmldesigner/designmodecontext.h b/src/plugins/qmldesigner/designmodecontext.h index 72c0a195239..12f0113d977 100644 --- a/src/plugins/qmldesigner/designmodecontext.h +++ b/src/plugins/qmldesigner/designmodecontext.h @@ -74,15 +74,6 @@ public: void contextHelp(const Core::IContext::HelpCallback &callback) const override; }; -class EffectMakerContext : public Core::IContext -{ - Q_OBJECT - -public: - EffectMakerContext(QWidget *widget); - void contextHelp(const Core::IContext::HelpCallback &callback) const override; -}; - class CollectionEditorContext : public Core::IContext { Q_OBJECT From 461340d2459f8841952101ab358a74dd7e180480 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 18 Sep 2023 16:26:57 +0300 Subject: [PATCH 032/130] QmlDesigner: Tie 3D helper grid to snap interval Helper grid minimum step now changes to match set snap interval. Fixes: QDS-10624 Change-Id: I82b8206774b3769bee19be5eb0f38930f3b49c12 Reviewed-by: Mahmoud Badri --- .../qml2puppet/mockfiles/qt6/HelperGrid.qml | 19 ++++++++----------- .../mockfiles/shaders/gridmaterial.frag | 5 +++++ .../qml2puppet/editor3d/generalhelper.cpp | 14 ++++++++++++++ .../qml2puppet/editor3d/generalhelper.h | 7 ++++++- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml b/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml index 2a7f921ffe2..e9d94b54c18 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml @@ -13,9 +13,8 @@ Node { property bool orthoMode: false property double distance: 500 - readonly property int minGridStep: 50 - readonly property int maxGridStep: 32 * minGridStep - readonly property int gridArea: minGridStep * 512 + readonly property int maxGridStep: 32 * _generalHelper.minGridStep + readonly property int gridArea: _generalHelper.minGridStep * 512 // Step of the main lines of the grid, between those is always one subdiv line property int gridStep: 100 @@ -32,12 +31,13 @@ Node { return Math.atan(step / distance) } - onDistanceChanged: { + function calcStep() + { if (distance === 0) return // Calculate new grid step - let newStep = gridStep + let newStep = _generalHelper.minGridStep let gridRad = calcRad(newStep) while (gridRad < minGridRad && newStep < maxGridStep) { newStep *= 2 @@ -45,16 +45,13 @@ Node { newStep = maxGridStep gridRad = calcRad(newStep) } - while (gridRad > minGridRad * 2 && newStep > minGridStep) { - newStep /= 2 - if (newStep < minGridStep) - newStep = minGridStep - gridRad = calcRad(newStep) - } gridStep = newStep subGridMaterial.generalAlpha = Math.min(1, 2 * (1 - (minGridRad / gridRad))) } + onMaxGridStepChanged: calcStep() + onDistanceChanged: calcStep() + // Note: Only one instance of HelperGrid is supported, as the geometry names are fixed Model { // Main grid lines diff --git a/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag b/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag index 6ec50859028..a1ec3bffff2 100644 --- a/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag +++ b/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag @@ -14,6 +14,11 @@ void MAIN() float cosAngle = dot(normalize(camDir), normalize(camLevel)); float angleDepth = density * pow(cosAngle, 8); float alpha = generalAlpha * clamp((1.0 - ((angleDepth * depth - alphaStartDepth) / (alphaEndDepth - alphaStartDepth))), 0, 1); + + // Force additional alpha when approaching the far clip of edit camera + if (depth > 90000.0) + alpha *= clamp((100000.0 - depth) / 10000.0, 0, 1); + if (alpha > 0.01) FRAGCOLOR = vec4(color.x * alpha, color.y * alpha, color.z * alpha, alpha); else diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 47f176b4219..e39113d774b 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -954,6 +954,20 @@ QVector3D GeneralHelper::adjustScaleForSnap(const QVector3D &newScale) return adjScale; } +void GeneralHelper::setSnapPositionInterval(double interval) +{ + if (m_snapPositionInterval != interval) { + m_snapPositionInterval = interval; + emit minGridStepChanged(); + } +} + +double GeneralHelper::minGridStep() const +{ + // Minimum grid step is a multiple of snap interval, as the last step is divided to subgrid + return 2. * m_snapPositionInterval; +} + void GeneralHelper::setBgColor(const QVariant &colors) { if (m_bgColor != colors) { diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index a80ab516ae2..8daa10d2ce6 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -33,6 +33,7 @@ class GeneralHelper : public QObject Q_OBJECT Q_PROPERTY(bool isMacOS READ isMacOS CONSTANT) Q_PROPERTY(QVariant bgColor READ bgColor NOTIFY bgColorChanged FINAL) + Q_PROPERTY(double minGridStep READ minGridStep NOTIFY minGridStepChanged FINAL) public: GeneralHelper(); @@ -114,10 +115,12 @@ public: void setSnapPosition(bool enable) { m_snapPosition = enable; } void setSnapRotation(bool enable) { m_snapRotation = enable; } void setSnapScale(bool enable) { m_snapScale = enable; } - void setSnapPositionInterval(double interval) { m_snapPositionInterval = interval; } + void setSnapPositionInterval(double interval); void setSnapRotationInterval(double interval) { m_snapRotationInterval = interval; } void setSnapScaleInterval(double interval) { m_snapScaleInterval = interval / 100.; } + double minGridStep() const; + void setBgColor(const QVariant &colors); QVariant bgColor() const { return m_bgColor; } @@ -128,6 +131,8 @@ signals: void lockedStateChanged(QQuick3DNode *node); void rotationBlocksChanged(); void bgColorChanged(); + void minGridStepChanged(); + private: void handlePendingToolStateUpdate(); QVector3D pivotScenePosition(QQuick3DNode *node) const; From 5ff6fe0eba9934febfb84fae324503b8840e1bbb Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 18 Sep 2023 15:52:12 +0200 Subject: [PATCH 033/130] QmlDesigner: Add missing qualification This was leading to random errors. Change-Id: I51ec0c71ac1bd3ea7bd966eeb4d3a2b9cfeb0159 Reviewed-by: Tim Jenssen --- .../qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml index 91945ded88e..8f2ee2f41f4 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml @@ -135,7 +135,7 @@ Controls.Popup { boundsMovement: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds - ScrollBar.vertical: HelperWidgets.ScrollBar { + Controls.ScrollBar.vertical: HelperWidgets.ScrollBar { id: listScrollBar parent: listView x: listView.width - listScrollBar.width From e0441a9aecc941efac454f72767df897ed718727 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 18 Sep 2023 15:51:29 +0200 Subject: [PATCH 034/130] QmlDesigner: Choose new target from filtered targets Task-number: QDS-10137 Change-Id: Ic74262862996c73ab5119793b92575035d590f60 Reviewed-by: Tim Jenssen Reviewed-by: Thomas Hartmann --- .../components/toolbar/toolbarbackend.cpp | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index b2f94d4df4b..1a8d3835fa7 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -504,17 +504,33 @@ void ToolBarBackend::setCurrentStyle(int index) view->resetPuppet(); } +ProjectExplorer::Kit *kitForDisplayName(const QString &displayName) +{ + const auto kits = ProjectExplorer::KitManager::kits(); + + for (auto kit : kits) { + if (kit->displayName() == displayName) + return kit; + } + + return {}; +} + void ToolBarBackend::setCurrentKit(int index) { auto project = ProjectExplorer::ProjectManager::startupProject(); QTC_ASSERT(project, return ); - const auto kits = ProjectExplorer::KitManager::kits(); + const auto kits = ToolBarBackend::kits(); - QTC_ASSERT(kits.size() > index, return); + QTC_ASSERT(kits.size() > index, return ); QTC_ASSERT(index >= 0, return ); - const auto kit = kits.at(index); + const auto kitDisplayName = kits.at(index); + + const auto kit = kitForDisplayName(kitDisplayName); + + QTC_ASSERT(kit, return ); auto newTarget = project->target(kit); if (!newTarget) From 2a135f5d1456d0de871ab78457991e1d62530102 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 18 Sep 2023 15:41:54 +0200 Subject: [PATCH 035/130] QmlDesigner: Fix long expression pill texts * Elide static texts * Clip dynamic texts Change-Id: I90a511ec6dd35d2b65f4064bc09c0a83e8d1e905 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../connectionseditor/ExpressionBuilder.qml | 1 + .../qmldesigner/connectionseditor/Pill.qml | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml index bd367debb75..54d8ee937f3 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml @@ -306,6 +306,7 @@ Rectangle { id: pill operatorModel: __operatorModel + maxTextWidth: root.width - 2 * StudioTheme.Values.flowMargin onRemove: function() { // If pill has focus due to selection or keyboard navigation diff --git a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml index 3862e53e5ce..1a6d2681f50 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml @@ -36,6 +36,8 @@ FocusScope { property bool hovered: rootMouseArea.containsMouse property bool selected: root.focus + property int maxTextWidth: 600 + width: row.width height: StudioTheme.Values.flowPillHeight @@ -111,10 +113,23 @@ FocusScope { leftPadding: root.margin rightPadding: icon.visible ? 0 : root.margin + property int textWidth: Math.min(textMetrics.width, + root.maxTextWidth - row.leftPadding + - (icon.visible ? icon.width : root.margin)) + + TextMetrics { + id: textMetrics + text: textItem.visible ? textItem.text : textInput.text + font: textItem.font + } + Text { id: textItem + width: row.textWidth height: StudioTheme.Values.flowPillHeight verticalAlignment: Text.AlignVCenter + textFormat: Text.PlainText + elide: Text.ElideMiddle font.pixelSize: StudioTheme.Values.baseFontSize color: root.isShadow() ? StudioTheme.Values.themePillTextSelected : StudioTheme.Values.themePillText @@ -125,10 +140,11 @@ FocusScope { TextInput { id: textInput - x: root.isInvalid() ? root.margin : 0 + width: row.textWidth height: StudioTheme.Values.flowPillHeight topPadding: 1 + clip: true font.pixelSize: StudioTheme.Values.baseFontSize color: { if (root.isIntermediate()) From 0547cff092209e8680c8adf6d90952169e5bbf0b Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 18 Sep 2023 17:52:34 +0200 Subject: [PATCH 036/130] QmlDesigner: Update branch for designer components Change-Id: I370fcd7eb15a26d28e60dc8bf2cdf24c29a43957 Reviewed-by: Thomas Hartmann --- .../studio_templates/projects/common/qmlcomponents.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl index 5e2d5923b1e..81f2ceba948 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl @@ -8,7 +8,7 @@ set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml") include(FetchContent) FetchContent_Declare( ds - GIT_TAG qds-4.1 + GIT_TAG qds-4.3 GIT_REPOSITORY https://code.qt.io/qt-labs/qtquickdesigner-components.git ) From ffa4aba2c8d3b5cf30ddf609d7a9d8d6409551bd Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 18 Sep 2023 16:33:36 +0200 Subject: [PATCH 037/130] QmlDesigner: Adjust priorities Change-Id: I9e455b40eca108aee869a24606300f38d14b5326 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../components/connectioneditor/propertytreemodel.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index cd51def60a2..073b8214927 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -102,12 +102,17 @@ const std::vector priorityListSignals = {"clicked", "rotationChanged"}; const std::vector priorityListProperties = {"opacity", + "checked", + "hovered", "visible", "value", + "down", "x", "y", "width", "height", + "from", + "to", "rotation", "color", "scale", @@ -117,12 +122,9 @@ const std::vector priorityListProperties = {"opacity", "text", "pressed", "containsMouse", - "checked", - "hovered", "down", "clip", "parent", - "from", "radius", "smooth", "true", From 1b9ed62028a60a4cf155bca82cea553c8390952f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 18 Sep 2023 17:34:39 +0200 Subject: [PATCH 038/130] QmlDesigner: Close dialog if target is removed If the target is removed also the connection is removed and we close the dialog. Change-Id: I361e89ef64484e39bc0068e103f542cbc9dcbf78 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Aleksei German --- .../connectionseditor/ConnectionsDialog.qml | 10 +++++++++- .../components/connectioneditor/connectionmodel.cpp | 11 +++++++++++ .../components/connectioneditor/connectionmodel.h | 3 +++ .../components/connectioneditor/connectionview.cpp | 5 +++++ .../components/connectioneditor/connectionview.h | 1 + 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml index b880916e4d8..7405f82a47b 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml @@ -7,7 +7,7 @@ import StudioTheme 1.0 as StudioTheme import HelperWidgets 2.0 as HelperWidgets PopupDialog { - + id: root property alias backend: form.backend titleBar: Row { @@ -41,5 +41,13 @@ PopupDialog { ConnectionsDialogForm { id: form + + Connections { + target: root.backend + onPopupTargetRemoved: { + root.close() + } + } } + } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 9fca617e359..c0b819371a8 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -487,6 +487,17 @@ void ConnectionModel::selectProperty(const SignalHandlerProperty &property) } } +void ConnectionModel::nodeAboutToBeRemoved(const ModelNode &removedNode) +{ + SignalHandlerProperty selectedSignal = signalHandlerPropertyForRow(currentIndex()); + if (selectedSignal.isValid()) { + ModelNode targetNode = getTargetNodeForConnection(selectedSignal.parentModelNode()); + if (targetNode == removedNode) { + emit m_delegate->popupTargetRemoved(); + } + } +} + void ConnectionModel::handleException() { QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index 03ee4e7144c..d9b13cb22d2 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -71,6 +71,8 @@ public: void selectProperty(const SignalHandlerProperty &property); + void nodeAboutToBeRemoved(const ModelNode &removedNode); + signals: void currentIndexChanged(); @@ -296,6 +298,7 @@ signals: void hasConditionChanged(); void hasElseChanged(); void sourceChanged(); + void popupTargetRemoved(); private: int currentRow() const; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 41eee97be48..0b02ee0e34b 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -176,6 +176,11 @@ void ConnectionView::nodeCreated(const ModelNode & /*createdNode*/) connectionModel()->resetModel(); } +void ConnectionView::nodeAboutToBeRemoved(const ModelNode &removedNode) +{ + connectionModel()->nodeAboutToBeRemoved(removedNode); +} + void ConnectionView::nodeRemoved(const ModelNode & /*removedNode*/, const NodeAbstractProperty & /*parentProperty*/, AbstractView::PropertyChangeFlags /*propertyChange*/) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h index b865bf51cc4..338d1fb14ed 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h @@ -39,6 +39,7 @@ public: void modelAboutToBeDetached(Model *model) override; void nodeCreated(const ModelNode &createdNode) override; + void nodeAboutToBeRemoved(const ModelNode &removedNode) override; void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override; void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override; From 94bd14eb46f0fbcad2af8e643a479e7b674b8e94 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 19 Sep 2023 11:02:14 +0200 Subject: [PATCH 039/130] QmlDesigner: Bump QDS version in template Change-Id: If05eb88eabd59d0a63944887f896e32da2b9c202 Reviewed-by: Thomas Hartmann --- .../studio_templates/projects/common/app.qmlproject.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl index 0b96ccf3ba3..50840bae070 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl @@ -105,7 +105,7 @@ Project { /* Required for deployment */ targetDirectory: "/opt/%{ProjectName}" - qdsVersion: "4.2" + qdsVersion: "4.3" quickVersion: "%{QtQuickVersion}" From 560b77d64b2fde5d30e8f0f468228411a37c63cb Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 19 Sep 2023 11:49:27 +0200 Subject: [PATCH 040/130] QmlDesigner: Fix Connections in ConnectionsEditor Fix Implicitly defined onFoo properties in Connections are deprecated. Change-Id: I0f2460dd4b69c070f4982ca0ec13b19cfda0342b Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../connectionseditor/ConnectionsDialog.qml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml index 7405f82a47b..131ae6cee99 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml @@ -8,7 +8,7 @@ import HelperWidgets 2.0 as HelperWidgets PopupDialog { id: root - property alias backend: form.backend + property alias backend: form.backend titleBar: Row { spacing: 30 // TODO @@ -36,18 +36,16 @@ PopupDialog { property int currentTypeIndex: backend.signal.id.currentIndex ?? 0 onCurrentTypeIndexChanged: target.currentIndex = target.currentTypeIndex } - } ConnectionsDialogForm { - id: form + id: form - Connections { - target: root.backend - onPopupTargetRemoved: { - root.close() - } - } + Connections { + target: root.backend + function onPopupTargetRemoved() { + root.close() + } + } } - } From f227dbf041e372c98319363615868088bb15fdc0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 19 Sep 2023 12:18:41 +0200 Subject: [PATCH 041/130] QmlDesigner: Fix appearance main toolbar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using _q_custom_style_disabled had slight unwanted impact on appearance. Introducing _q_custom_style_skipolish instead in studio style. Using studio style whenever we are Qt Design Studio for all themes. Change-Id: Iee460cfc0a62122b1aeb6d97746808658c4f3ebf Reviewed-by: Henning Gründl --- src/plugins/qmldesigner/components/toolbar/toolbar.cpp | 3 ++- src/plugins/qmldesigner/designmodewidget.cpp | 4 +++- src/plugins/qmldesignerbase/studio/studiostyle.cpp | 7 +++++++ src/plugins/qmldesignerbase/studio/studiostyle.h | 2 ++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp index 0190a665f70..5facbdda571 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp @@ -65,7 +65,8 @@ Utils::UniqueObjectPtr ToolBar::create() toolBar->setFloatable(false); toolBar->setMovable(false); - toolBar->setProperty("_q_custom_style_disabled", true); + toolBar->setProperty("_q_custom_style_skipolish", true); + toolBar->setContentsMargins(0, 0, 0, 0); auto quickWidget = std::make_unique(); diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index 822b23fc9a7..c42dd0673c6 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -34,6 +34,8 @@ #include #include +#include + #include #include #include @@ -95,7 +97,7 @@ DesignModeWidget::DesignModeWidget() , m_crumbleBar(new CrumbleBar(this)) { setAcceptDrops(true); - if (Utils::StyleHelper::isQDSTheme()) + if (Utils::StyleHelper::isQDSTheme() || QmlProjectManager::QmlProject::isQtDesignStudio()) qApp->setStyle(QmlDesignerBasePlugin::style()); } diff --git a/src/plugins/qmldesignerbase/studio/studiostyle.cpp b/src/plugins/qmldesignerbase/studio/studiostyle.cpp index 59f19e9f5c7..045ef43fc42 100644 --- a/src/plugins/qmldesignerbase/studio/studiostyle.cpp +++ b/src/plugins/qmldesignerbase/studio/studiostyle.cpp @@ -1136,6 +1136,13 @@ QPalette StudioStyle::standardPalette() const return d->stdPalette; } +void StudioStyle::polish(QWidget *widget) +{ + if (widget && widget->property("_q_custom_style_skipolish").toBool()) + return; + Super::polish(widget); +} + void StudioStyle::drawQmlEditorIcon( PrimitiveElement element, const QStyleOption *option, diff --git a/src/plugins/qmldesignerbase/studio/studiostyle.h b/src/plugins/qmldesignerbase/studio/studiostyle.h index 63250a007de..0912d3071fd 100644 --- a/src/plugins/qmldesignerbase/studio/studiostyle.h +++ b/src/plugins/qmldesignerbase/studio/studiostyle.h @@ -58,6 +58,8 @@ public: QPalette standardPalette() const override; + void polish(QWidget *widget) override; + private: void drawQmlEditorIcon(PrimitiveElement element, const QStyleOption *option, From 5d7808f95b66d09e67dacadab18795054f4316f4 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 19 Sep 2023 11:57:10 +0300 Subject: [PATCH 042/130] QmlDesigner: Don't show context on 2D view when editing 3D component Fixes: QDS-10664 Change-Id: Ia4436b942ce79e0abe622adbba914903ab6a72bc Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../qmldesigner/components/formeditor/formeditorview.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index 6b580a7d5f3..2a433e6764a 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -652,9 +652,8 @@ void FormEditorView::auxiliaryDataChanged(const ModelNode &node, editorItem->setFrameColor(data.value()); } - if (key == contextImageProperty) { + if (key == contextImageProperty && !Qml3DNode::isValidVisualRoot(rootModelNode())) m_formEditorWidget->setBackgoundImage(data.value()); - } } static void updateTransitions(FormEditorScene *scene, const QmlItemNode &qmlItemNode) From f5e01b8b099c8b401819ceb3c6430aada93578e1 Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Tue, 19 Sep 2023 11:57:38 +0200 Subject: [PATCH 043/130] QmlDesigner: Fix Pill eliding logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I573251001b2e77ff3f3fb0046615d65de814c21d Reviewed-by: Thomas Hartmann Reviewed-by: Henning Gründl Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- share/qtcreator/qmldesigner/connectionseditor/Pill.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml index 1a6d2681f50..d92163e9096 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml @@ -113,7 +113,7 @@ FocusScope { leftPadding: root.margin rightPadding: icon.visible ? 0 : root.margin - property int textWidth: Math.min(textMetrics.width, + property int textWidth: Math.min(textMetrics.advanceWidth + 2, root.maxTextWidth - row.leftPadding - (icon.visible ? icon.width : root.margin)) From e156854fc38b61fcfa1b7aec533d92b5055663dd Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 19 Sep 2023 14:13:56 +0200 Subject: [PATCH 044/130] QmlDesigner: One more missing qualification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I394e58b2cccdfbfcacd13ff2c5cefba8d4719ef4 Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- .../qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml index 8f2ee2f41f4..332059fbc6c 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml @@ -195,7 +195,7 @@ Controls.Popup { boundsMovement: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds - ScrollBar.vertical: HelperWidgets.ScrollBar { + Controls.ScrollBar.vertical: HelperWidgets.ScrollBar { id: treeScrollBar parent: treeView x: treeView.width - treeScrollBar.width From 096c825084f52b2c292ada10fec8269c027e029c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 19 Sep 2023 11:14:31 +0200 Subject: [PATCH 045/130] QmlJSCheck: Do not warn about visual properties in Connections Change-Id: I1b8d9374021d337d87025290fd025dd600fc3967 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tim Jenssen --- src/libs/qmljs/qmljscheck.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 9aaf5c02a19..feab41f24bf 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -1442,7 +1442,11 @@ bool Check::visit(BinaryExpression *ast) SourceLocation expressionSourceLocation = locationFromRange(ast->firstSourceLocation(), ast->lastSourceLocation()); - if (expressionAffectsVisualAspects(ast)) + + const bool isDirectInConnectionsScope = (!m_typeStack.isEmpty() + && m_typeStack.last() == "Connections"); + + if (expressionAffectsVisualAspects(ast) && !isDirectInConnectionsScope) addMessage(WarnImperativeCodeNotEditableInVisualDesigner, expressionSourceLocation); // check ==, != From 0ef6c184c12254cee3f79df85e0f31e46f6d11a8 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 19 Sep 2023 11:02:50 +0200 Subject: [PATCH 046/130] QmlProject: Add new options to cmakefiles.txt template If the template was regernated (which is not the default), it was missing features. Change-Id: I446dd454127795e0e7edcb6669dc7025ce37be01 Reviewed-by: Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../cmakegen/qmlprojectmaincmakelists.tpl | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl index 423ed1a921a..4cd900a31ff 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl +++ b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl @@ -1,11 +1,19 @@ -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.21.1) + +option(LINK_INSIGHT "Link Qt Insight Tracker library" ON) +option(BUILD_QDS_COMPONENTS "Build design studio components" ON) project(%1 LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) -find_package(Qt6 COMPONENTS Gui Qml Quick) +find_package(Qt6 6.2 REQUIRED COMPONENTS Core Gui Qml Quick) + +if (Qt6_VERSION VERSION_GREATER_EQUAL 6.3) + qt_standard_project_setup() +endif() + qt_add_executable(%1 src/main.cpp) qt_add_resources(%1 "configuration" @@ -20,5 +28,20 @@ target_link_libraries(%1 PRIVATE Qt${QT_VERSION_MAJOR}::Qml ) +if (BUILD_QDS_COMPONENTS) + include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents) +endif() + include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules) +if (LINK_INSIGHT) + include(${CMAKE_CURRENT_SOURCE_DIR}/insight) +endif () + +include(GNUInstallDirs) +install(TARGETS CppExampleApp + BUNDLE DESTINATION . + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + From 37c4937d6d14e8d9fbce5db70654a1d54da6a01a Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Mon, 18 Sep 2023 13:30:25 +0200 Subject: [PATCH 047/130] QmlDesigner: Add new icons * Add new icons * Change position code editor button * Add eliding to the code preview Change-Id: Ib47b0e28ae9b4deebc7bace942f43d8249f46299 Reviewed-by: Thomas Hartmann Reviewed-by: Brook Cronin Reviewed-by: Qt CI Patch Build Bot --- .../ConnectionsDialogForm.qml | 25 +- .../imports/StudioTheme/InternalConstants.qml | 545 +++++++++--------- .../imports/StudioTheme/icons.ttf | Bin 59344 -> 59592 bytes .../components/componentcore/theme.h | 1 + 4 files changed, 283 insertions(+), 288 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 075c97641a1..57e34e81c9c 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -212,6 +212,13 @@ Column { && backend.hasCondition && backend.hasElse } + HelperWidgets.AbstractButton { + id: editorButton + buttonIcon: StudioTheme.Constants.codeEditor_medium + tooltip: qsTr("Add something.") + onClicked: expressionDialogLoader.show() + } + // Editor Rectangle { id: editor @@ -226,24 +233,10 @@ Column { text: backend.indentedSource color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.myFontSize - wrapMode: Text.WordWrap + wrapMode: Text.Wrap horizontalAlignment: code.lineCount === 1 ? Text.AlignHCenter : Text.AlignLeft verticalAlignment: Text.AlignVCenter - } - - HelperWidgets.AbstractButton { - id: editorButton - - anchors.top: parent.top - anchors.right: parent.right - anchors.margins: 4 - - style: StudioTheme.Values.viewBarButtonStyle - buttonIcon: StudioTheme.Constants.edit_medium - tooltip: qsTr("Add something.") - onClicked: { - expressionDialogLoader.show() - } + elide: Text.ElideRight } Loader { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index 0feb5063441..23c37e165f2 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -75,278 +75,279 @@ QtObject { readonly property string closeLink: "\u005C" readonly property string close_small: "\u005D" readonly property string code: "\u005E" - readonly property string colorPopupClose: "\u005F" - readonly property string colorSelection_medium: "\u0060" - readonly property string columnsAndRows: "\u0061" - readonly property string cone_medium: "\u0062" - readonly property string cone_small: "\u0063" - readonly property string connection_small: "\u0064" - readonly property string connections_medium: "\u0065" - readonly property string copyLink: "\u0066" - readonly property string copyStyle: "\u0067" - readonly property string copy_small: "\u0068" - readonly property string cornerA: "\u0069" - readonly property string cornerB: "\u006A" - readonly property string cornersAll: "\u006B" - readonly property string createComponent_large: "\u006C" - readonly property string createComponent_small: "\u006D" - readonly property string create_medium: "\u006E" - readonly property string create_small: "\u006F" - readonly property string cube_medium: "\u0070" - readonly property string cube_small: "\u0071" - readonly property string curveDesigner: "\u0072" - readonly property string curveDesigner_medium: "\u0073" - readonly property string curveEditor: "\u0074" - readonly property string customMaterialEditor: "\u0075" - readonly property string cylinder_medium: "\u0076" - readonly property string cylinder_small: "\u0077" - readonly property string decisionNode: "\u0078" - readonly property string deleteColumn: "\u0079" - readonly property string deleteMaterial: "\u007A" - readonly property string deleteRow: "\u007B" - readonly property string deleteTable: "\u007C" - readonly property string delete_medium: "\u007D" - readonly property string delete_small: "\u007E" - readonly property string designMode_large: "\u007F" - readonly property string detach: "\u0080" - readonly property string directionalLight_small: "\u0081" - readonly property string distributeBottom: "\u0082" - readonly property string distributeCenterHorizontal: "\u0083" - readonly property string distributeCenterVertical: "\u0084" - readonly property string distributeLeft: "\u0085" - readonly property string distributeOriginBottomRight: "\u0086" - readonly property string distributeOriginCenter: "\u0087" - readonly property string distributeOriginNone: "\u0088" - readonly property string distributeOriginTopLeft: "\u0089" - readonly property string distributeRight: "\u008A" - readonly property string distributeSpacingHorizontal: "\u008B" - readonly property string distributeSpacingVertical: "\u008C" - readonly property string distributeTop: "\u008D" - readonly property string download: "\u008E" - readonly property string downloadUnavailable: "\u008F" - readonly property string downloadUpdate: "\u0090" - readonly property string downloaded: "\u0091" - readonly property string dragmarks: "\u0092" - readonly property string duplicate_small: "\u0093" - readonly property string edit: "\u0094" - readonly property string editComponent_large: "\u0095" - readonly property string editComponent_small: "\u0096" - readonly property string editLightOff_medium: "\u0097" - readonly property string editLightOn_medium: "\u0098" - readonly property string edit_medium: "\u0099" - readonly property string edit_small: "\u009A" - readonly property string effects: "\u009B" - readonly property string events_small: "\u009D" - readonly property string export_medium: "\u009E" - readonly property string eyeDropper: "\u009F" - readonly property string favorite: "\u00A0" - readonly property string fitAll_medium: "\u00A1" - readonly property string fitSelected_small: "\u00A2" - readonly property string fitSelection_medium: "\u00A3" - readonly property string fitToView_medium: "\u00A4" - readonly property string flowAction: "\u00A5" - readonly property string flowTransition: "\u00A6" - readonly property string fontStyleBold: "\u00A7" - readonly property string fontStyleItalic: "\u00A8" - readonly property string fontStyleStrikethrough: "\u00A9" - readonly property string fontStyleUnderline: "\u00AA" - readonly property string forward_medium: "\u00AB" - readonly property string globalOrient_medium: "\u00AC" - readonly property string gradient: "\u00AE" - readonly property string gridView: "\u00AF" - readonly property string grid_medium: "\u00B0" - readonly property string group_small: "\u00B1" - readonly property string help: "\u00B2" - readonly property string home_large: "\u00B3" - readonly property string idAliasOff: "\u00B4" - readonly property string idAliasOn: "\u00B5" - readonly property string import_medium: "\u00B6" - readonly property string imported: "\u00B7" - readonly property string importedModels_small: "\u00B8" - readonly property string infinity: "\u00B9" - readonly property string invisible_medium: "\u00BA" - readonly property string invisible_small: "\u00BB" - readonly property string keyframe: "\u00BC" - readonly property string languageList_medium: "\u00BD" - readonly property string layouts_small: "\u00BE" - readonly property string lights_small: "\u00BF" - readonly property string linear_medium: "\u00C0" - readonly property string linkTriangle: "\u00C1" - readonly property string linked: "\u00C2" - readonly property string listView: "\u00C3" - readonly property string list_medium: "\u00C4" - readonly property string localOrient_medium: "\u00C5" - readonly property string lockOff: "\u00C6" - readonly property string lockOn: "\u00C7" - readonly property string loopPlayback_medium: "\u00C8" - readonly property string materialBrowser_medium: "\u00C9" - readonly property string materialPreviewEnvironment: "\u00CA" - readonly property string materialPreviewModel: "\u00CB" - readonly property string material_medium: "\u00CC" - readonly property string maxBar_small: "\u00CD" - readonly property string mergeCells: "\u00CE" - readonly property string merge_small: "\u00CF" - readonly property string minus: "\u00D0" - readonly property string mirror: "\u00D1" - readonly property string more_medium: "\u00D2" - readonly property string mouseArea_small: "\u00D3" - readonly property string moveDown_medium: "\u00D4" - readonly property string moveInwards_medium: "\u00D5" - readonly property string moveUp_medium: "\u00D6" - readonly property string moveUpwards_medium: "\u00D7" - readonly property string move_medium: "\u00D8" - readonly property string newMaterial: "\u00D9" - readonly property string nextFile_large: "\u00DA" - readonly property string normalBar_small: "\u00DB" - readonly property string openLink: "\u00DC" - readonly property string openMaterialBrowser: "\u00DD" - readonly property string orientation: "\u00DE" - readonly property string orthCam_medium: "\u00DF" - readonly property string orthCam_small: "\u00E0" - readonly property string paddingEdge: "\u00E1" - readonly property string paddingFrame: "\u00E2" - readonly property string particleAnimation_medium: "\u00E3" - readonly property string pasteStyle: "\u00E4" - readonly property string paste_small: "\u00E5" - readonly property string pause: "\u00E6" - readonly property string perspectiveCam_medium: "\u00E7" - readonly property string perspectiveCam_small: "\u00E8" - readonly property string pin: "\u00E9" - readonly property string plane_medium: "\u00EA" - readonly property string plane_small: "\u00EB" - readonly property string play: "\u00EC" - readonly property string playFill_medium: "\u00ED" - readonly property string playOutline_medium: "\u00EE" - readonly property string plus: "\u00EF" - readonly property string pointLight_small: "\u00F0" - readonly property string positioners_small: "\u00F1" - readonly property string previewEnv_medium: "\u00F2" - readonly property string previousFile_large: "\u00F3" - readonly property string promote: "\u00F4" - readonly property string properties_medium: "\u00F5" - readonly property string readOnly: "\u00F6" - readonly property string recordFill_medium: "\u00F7" - readonly property string recordOutline_medium: "\u00F8" - readonly property string redo: "\u00F9" - readonly property string reload_medium: "\u00FA" - readonly property string remove_medium: "\u00FB" - readonly property string remove_small: "\u00FC" - readonly property string rename_small: "\u00FD" - readonly property string replace_small: "\u00FE" - readonly property string resetView_small: "\u00FF" - readonly property string restartParticles_medium: "\u0100" - readonly property string reverseOrder_medium: "\u0101" - readonly property string roatate_medium: "\u0102" - readonly property string rotationFill: "\u0103" - readonly property string rotationOutline: "\u0104" - readonly property string runProjFill_large: "\u0105" - readonly property string runProjOutline_large: "\u0106" - readonly property string s_anchors: "\u0107" - readonly property string s_annotations: "\u0108" - readonly property string s_arrange: "\u0109" - readonly property string s_boundingBox: "\u010A" - readonly property string s_component: "\u010B" - readonly property string s_connections: "\u010C" - readonly property string s_edit: "\u010D" - readonly property string s_enterComponent: "\u010E" - readonly property string s_eventList: "\u010F" - readonly property string s_group: "\u0110" - readonly property string s_layouts: "\u0111" - readonly property string s_merging: "\u0112" - readonly property string s_mouseArea: "\u0113" - readonly property string s_positioners: "\u0114" - readonly property string s_selection: "\u0115" - readonly property string s_snapping: "\u0116" - readonly property string s_timeline: "\u0117" - readonly property string s_visibility: "\u0118" - readonly property string saveLogs_medium: "\u0119" - readonly property string scale_medium: "\u011A" - readonly property string search: "\u011B" - readonly property string search_small: "\u011C" - readonly property string sectionToggle: "\u011D" - readonly property string selectFill_medium: "\u011E" - readonly property string selectOutline_medium: "\u011F" - readonly property string selectParent_small: "\u0120" - readonly property string selection_small: "\u0121" - readonly property string settings_medium: "\u0122" - readonly property string signal_small: "\u0123" - readonly property string snapping_conf_medium: "\u0124" - readonly property string snapping_medium: "\u0125" - readonly property string snapping_small: "\u0126" - readonly property string sphere_medium: "\u0127" - readonly property string sphere_small: "\u0128" - readonly property string splitColumns: "\u0129" - readonly property string splitRows: "\u012A" - readonly property string spotLight_small: "\u012B" - readonly property string stackedContainer_small: "\u012C" - readonly property string startNode: "\u012D" - readonly property string step_medium: "\u012E" - readonly property string stop_medium: "\u012F" - readonly property string testIcon: "\u0130" - readonly property string textAlignBottom: "\u0131" - readonly property string textAlignCenter: "\u0132" - readonly property string textAlignJustified: "\u0133" - readonly property string textAlignLeft: "\u0134" - readonly property string textAlignMiddle: "\u0135" - readonly property string textAlignRight: "\u0136" - readonly property string textAlignTop: "\u0137" - readonly property string textBulletList: "\u0138" - readonly property string textFullJustification: "\u0139" - readonly property string textNumberedList: "\u013A" - readonly property string textures_medium: "\u013B" - readonly property string tickIcon: "\u013C" - readonly property string tickMark_small: "\u013D" - readonly property string timeline_small: "\u013E" - readonly property string toEndFrame_medium: "\u013F" - readonly property string toNextFrame_medium: "\u0140" - readonly property string toPrevFrame_medium: "\u0141" - readonly property string toStartFrame_medium: "\u0142" - readonly property string topToolbar_annotations: "\u0143" - readonly property string topToolbar_closeFile: "\u0144" - readonly property string topToolbar_designMode: "\u0145" - readonly property string topToolbar_enterComponent: "\u0146" - readonly property string topToolbar_home: "\u0147" - readonly property string topToolbar_makeComponent: "\u0148" - readonly property string topToolbar_navFile: "\u0149" - readonly property string topToolbar_runProject: "\u014A" - readonly property string translationCreateFiles: "\u014B" - readonly property string translationCreateReport: "\u014C" - readonly property string translationExport: "\u014D" - readonly property string translationImport: "\u014E" - readonly property string translationSelectLanguages: "\u014F" - readonly property string translationTest: "\u0150" - readonly property string transparent: "\u0151" - readonly property string triState: "\u0152" - readonly property string triangleArcA: "\u0153" - readonly property string triangleArcB: "\u0154" - readonly property string triangleCornerA: "\u0155" - readonly property string triangleCornerB: "\u0156" - readonly property string unLinked: "\u0157" - readonly property string undo: "\u0158" - readonly property string unify_medium: "\u0159" - readonly property string unpin: "\u015A" - readonly property string upDownIcon: "\u015B" - readonly property string upDownSquare2: "\u015C" - readonly property string updateAvailable_medium: "\u015D" - readonly property string updateContent_medium: "\u015E" - readonly property string visibilityOff: "\u015F" - readonly property string visibilityOn: "\u0160" - readonly property string visible_medium: "\u0161" - readonly property string visible_small: "\u0162" - readonly property string wildcard: "\u0163" - readonly property string wizardsAutomotive: "\u0164" - readonly property string wizardsDesktop: "\u0165" - readonly property string wizardsGeneric: "\u0166" - readonly property string wizardsMcuEmpty: "\u0167" - readonly property string wizardsMcuGraph: "\u0168" - readonly property string wizardsMobile: "\u0169" - readonly property string wizardsUnknown: "\u016A" - readonly property string zoomAll: "\u016B" - readonly property string zoomIn: "\u016C" - readonly property string zoomIn_medium: "\u016D" - readonly property string zoomOut: "\u016E" - readonly property string zoomOut_medium: "\u016F" - readonly property string zoomSelection: "\u0170" + readonly property string codeEditor_medium: "\u005F" + readonly property string colorPopupClose: "\u0060" + readonly property string colorSelection_medium: "\u0061" + readonly property string columnsAndRows: "\u0062" + readonly property string cone_medium: "\u0063" + readonly property string cone_small: "\u0064" + readonly property string connection_small: "\u0065" + readonly property string connections_medium: "\u0066" + readonly property string copyLink: "\u0067" + readonly property string copyStyle: "\u0068" + readonly property string copy_small: "\u0069" + readonly property string cornerA: "\u006A" + readonly property string cornerB: "\u006B" + readonly property string cornersAll: "\u006C" + readonly property string createComponent_large: "\u006D" + readonly property string createComponent_small: "\u006E" + readonly property string create_medium: "\u006F" + readonly property string create_small: "\u0070" + readonly property string cube_medium: "\u0071" + readonly property string cube_small: "\u0072" + readonly property string curveDesigner: "\u0073" + readonly property string curveDesigner_medium: "\u0074" + readonly property string curveEditor: "\u0075" + readonly property string customMaterialEditor: "\u0076" + readonly property string cylinder_medium: "\u0077" + readonly property string cylinder_small: "\u0078" + readonly property string decisionNode: "\u0079" + readonly property string deleteColumn: "\u007A" + readonly property string deleteMaterial: "\u007B" + readonly property string deleteRow: "\u007C" + readonly property string deleteTable: "\u007D" + readonly property string delete_medium: "\u007E" + readonly property string delete_small: "\u007F" + readonly property string designMode_large: "\u0080" + readonly property string detach: "\u0081" + readonly property string directionalLight_small: "\u0082" + readonly property string distributeBottom: "\u0083" + readonly property string distributeCenterHorizontal: "\u0084" + readonly property string distributeCenterVertical: "\u0085" + readonly property string distributeLeft: "\u0086" + readonly property string distributeOriginBottomRight: "\u0087" + readonly property string distributeOriginCenter: "\u0088" + readonly property string distributeOriginNone: "\u0089" + readonly property string distributeOriginTopLeft: "\u008A" + readonly property string distributeRight: "\u008B" + readonly property string distributeSpacingHorizontal: "\u008C" + readonly property string distributeSpacingVertical: "\u008D" + readonly property string distributeTop: "\u008E" + readonly property string download: "\u008F" + readonly property string downloadUnavailable: "\u0090" + readonly property string downloadUpdate: "\u0091" + readonly property string downloaded: "\u0092" + readonly property string dragmarks: "\u0093" + readonly property string duplicate_small: "\u0094" + readonly property string edit: "\u0095" + readonly property string editComponent_large: "\u0096" + readonly property string editComponent_small: "\u0097" + readonly property string editLightOff_medium: "\u0098" + readonly property string editLightOn_medium: "\u0099" + readonly property string edit_medium: "\u009A" + readonly property string edit_small: "\u009B" + readonly property string effects: "\u009D" + readonly property string events_small: "\u009E" + readonly property string export_medium: "\u009F" + readonly property string eyeDropper: "\u00A0" + readonly property string favorite: "\u00A1" + readonly property string fitAll_medium: "\u00A2" + readonly property string fitSelected_small: "\u00A3" + readonly property string fitSelection_medium: "\u00A4" + readonly property string fitToView_medium: "\u00A5" + readonly property string flowAction: "\u00A6" + readonly property string flowTransition: "\u00A7" + readonly property string fontStyleBold: "\u00A8" + readonly property string fontStyleItalic: "\u00A9" + readonly property string fontStyleStrikethrough: "\u00AA" + readonly property string fontStyleUnderline: "\u00AB" + readonly property string forward_medium: "\u00AC" + readonly property string globalOrient_medium: "\u00AE" + readonly property string gradient: "\u00AF" + readonly property string gridView: "\u00B0" + readonly property string grid_medium: "\u00B1" + readonly property string group_small: "\u00B2" + readonly property string help: "\u00B3" + readonly property string home_large: "\u00B4" + readonly property string idAliasOff: "\u00B5" + readonly property string idAliasOn: "\u00B6" + readonly property string import_medium: "\u00B7" + readonly property string imported: "\u00B8" + readonly property string importedModels_small: "\u00B9" + readonly property string infinity: "\u00BA" + readonly property string invisible_medium: "\u00BB" + readonly property string invisible_small: "\u00BC" + readonly property string keyframe: "\u00BD" + readonly property string languageList_medium: "\u00BE" + readonly property string layouts_small: "\u00BF" + readonly property string lights_small: "\u00C0" + readonly property string linear_medium: "\u00C1" + readonly property string linkTriangle: "\u00C2" + readonly property string linked: "\u00C3" + readonly property string listView: "\u00C4" + readonly property string list_medium: "\u00C5" + readonly property string localOrient_medium: "\u00C6" + readonly property string lockOff: "\u00C7" + readonly property string lockOn: "\u00C8" + readonly property string loopPlayback_medium: "\u00C9" + readonly property string materialBrowser_medium: "\u00CA" + readonly property string materialPreviewEnvironment: "\u00CB" + readonly property string materialPreviewModel: "\u00CC" + readonly property string material_medium: "\u00CD" + readonly property string maxBar_small: "\u00CE" + readonly property string mergeCells: "\u00CF" + readonly property string merge_small: "\u00D0" + readonly property string minus: "\u00D1" + readonly property string mirror: "\u00D2" + readonly property string more_medium: "\u00D3" + readonly property string mouseArea_small: "\u00D4" + readonly property string moveDown_medium: "\u00D5" + readonly property string moveInwards_medium: "\u00D6" + readonly property string moveUp_medium: "\u00D7" + readonly property string moveUpwards_medium: "\u00D8" + readonly property string move_medium: "\u00D9" + readonly property string newMaterial: "\u00DA" + readonly property string nextFile_large: "\u00DB" + readonly property string normalBar_small: "\u00DC" + readonly property string openLink: "\u00DD" + readonly property string openMaterialBrowser: "\u00DE" + readonly property string orientation: "\u00DF" + readonly property string orthCam_medium: "\u00E0" + readonly property string orthCam_small: "\u00E1" + readonly property string paddingEdge: "\u00E2" + readonly property string paddingFrame: "\u00E3" + readonly property string particleAnimation_medium: "\u00E4" + readonly property string pasteStyle: "\u00E5" + readonly property string paste_small: "\u00E6" + readonly property string pause: "\u00E7" + readonly property string perspectiveCam_medium: "\u00E8" + readonly property string perspectiveCam_small: "\u00E9" + readonly property string pin: "\u00EA" + readonly property string plane_medium: "\u00EB" + readonly property string plane_small: "\u00EC" + readonly property string play: "\u00ED" + readonly property string playFill_medium: "\u00EE" + readonly property string playOutline_medium: "\u00EF" + readonly property string plus: "\u00F0" + readonly property string pointLight_small: "\u00F1" + readonly property string positioners_small: "\u00F2" + readonly property string previewEnv_medium: "\u00F3" + readonly property string previousFile_large: "\u00F4" + readonly property string promote: "\u00F5" + readonly property string properties_medium: "\u00F6" + readonly property string readOnly: "\u00F7" + readonly property string recordFill_medium: "\u00F8" + readonly property string recordOutline_medium: "\u00F9" + readonly property string redo: "\u00FA" + readonly property string reload_medium: "\u00FB" + readonly property string remove_medium: "\u00FC" + readonly property string remove_small: "\u00FD" + readonly property string rename_small: "\u00FE" + readonly property string replace_small: "\u00FF" + readonly property string resetView_small: "\u0100" + readonly property string restartParticles_medium: "\u0101" + readonly property string reverseOrder_medium: "\u0102" + readonly property string roatate_medium: "\u0103" + readonly property string rotationFill: "\u0104" + readonly property string rotationOutline: "\u0105" + readonly property string runProjFill_large: "\u0106" + readonly property string runProjOutline_large: "\u0107" + readonly property string s_anchors: "\u0108" + readonly property string s_annotations: "\u0109" + readonly property string s_arrange: "\u010A" + readonly property string s_boundingBox: "\u010B" + readonly property string s_component: "\u010C" + readonly property string s_connections: "\u010D" + readonly property string s_edit: "\u010E" + readonly property string s_enterComponent: "\u010F" + readonly property string s_eventList: "\u0110" + readonly property string s_group: "\u0111" + readonly property string s_layouts: "\u0112" + readonly property string s_merging: "\u0113" + readonly property string s_mouseArea: "\u0114" + readonly property string s_positioners: "\u0115" + readonly property string s_selection: "\u0116" + readonly property string s_snapping: "\u0117" + readonly property string s_timeline: "\u0118" + readonly property string s_visibility: "\u0119" + readonly property string saveLogs_medium: "\u011A" + readonly property string scale_medium: "\u011B" + readonly property string search: "\u011C" + readonly property string search_small: "\u011D" + readonly property string sectionToggle: "\u011E" + readonly property string selectFill_medium: "\u011F" + readonly property string selectOutline_medium: "\u0120" + readonly property string selectParent_small: "\u0121" + readonly property string selection_small: "\u0122" + readonly property string settings_medium: "\u0123" + readonly property string signal_small: "\u0124" + readonly property string snapping_conf_medium: "\u0125" + readonly property string snapping_medium: "\u0126" + readonly property string snapping_small: "\u0127" + readonly property string sphere_medium: "\u0128" + readonly property string sphere_small: "\u0129" + readonly property string splitColumns: "\u012A" + readonly property string splitRows: "\u012B" + readonly property string spotLight_small: "\u012C" + readonly property string stackedContainer_small: "\u012D" + readonly property string startNode: "\u012E" + readonly property string step_medium: "\u012F" + readonly property string stop_medium: "\u0130" + readonly property string testIcon: "\u0131" + readonly property string textAlignBottom: "\u0132" + readonly property string textAlignCenter: "\u0133" + readonly property string textAlignJustified: "\u0134" + readonly property string textAlignLeft: "\u0135" + readonly property string textAlignMiddle: "\u0136" + readonly property string textAlignRight: "\u0137" + readonly property string textAlignTop: "\u0138" + readonly property string textBulletList: "\u0139" + readonly property string textFullJustification: "\u013A" + readonly property string textNumberedList: "\u013B" + readonly property string textures_medium: "\u013C" + readonly property string tickIcon: "\u013D" + readonly property string tickMark_small: "\u013E" + readonly property string timeline_small: "\u013F" + readonly property string toEndFrame_medium: "\u0140" + readonly property string toNextFrame_medium: "\u0141" + readonly property string toPrevFrame_medium: "\u0142" + readonly property string toStartFrame_medium: "\u0143" + readonly property string topToolbar_annotations: "\u0144" + readonly property string topToolbar_closeFile: "\u0145" + readonly property string topToolbar_designMode: "\u0146" + readonly property string topToolbar_enterComponent: "\u0147" + readonly property string topToolbar_home: "\u0148" + readonly property string topToolbar_makeComponent: "\u0149" + readonly property string topToolbar_navFile: "\u014A" + readonly property string topToolbar_runProject: "\u014B" + readonly property string translationCreateFiles: "\u014C" + readonly property string translationCreateReport: "\u014D" + readonly property string translationExport: "\u014E" + readonly property string translationImport: "\u014F" + readonly property string translationSelectLanguages: "\u0150" + readonly property string translationTest: "\u0151" + readonly property string transparent: "\u0152" + readonly property string triState: "\u0153" + readonly property string triangleArcA: "\u0154" + readonly property string triangleArcB: "\u0155" + readonly property string triangleCornerA: "\u0156" + readonly property string triangleCornerB: "\u0157" + readonly property string unLinked: "\u0158" + readonly property string undo: "\u0159" + readonly property string unify_medium: "\u015A" + readonly property string unpin: "\u015B" + readonly property string upDownIcon: "\u015C" + readonly property string upDownSquare2: "\u015D" + readonly property string updateAvailable_medium: "\u015E" + readonly property string updateContent_medium: "\u015F" + readonly property string visibilityOff: "\u0160" + readonly property string visibilityOn: "\u0161" + readonly property string visible_medium: "\u0162" + readonly property string visible_small: "\u0163" + readonly property string wildcard: "\u0164" + readonly property string wizardsAutomotive: "\u0165" + readonly property string wizardsDesktop: "\u0166" + readonly property string wizardsGeneric: "\u0167" + readonly property string wizardsMcuEmpty: "\u0168" + readonly property string wizardsMcuGraph: "\u0169" + readonly property string wizardsMobile: "\u016A" + readonly property string wizardsUnknown: "\u016B" + readonly property string zoomAll: "\u016C" + readonly property string zoomIn: "\u016D" + readonly property string zoomIn_medium: "\u016E" + readonly property string zoomOut: "\u016F" + readonly property string zoomOut_medium: "\u0170" + readonly property string zoomSelection: "\u0171" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index 75f1fb94fad07b083e695b22a6660a9d14d0512a..8e35e027799a14cc76cfd852c9bf2191461793f8 100644 GIT binary patch delta 1217 zcmca`p83Q@W)lWR1_lORh6V;^h5$FW5Z?uf)g}xKFV-+HFv$1^>l;PRNRwq?V2og3 zU`R;LO)Sv(ul;+XiBkQGB@7G<9O*fgX?4CoelRd_W-u@ujL1k$Owo8cg^huM;SK`> zgIPvKY9iOq|E&xRj3NvS3@RD9B^9#zBFPL4j5`<@nAYUvCnsKhvp9=^f$abT1JknH z#EJsOWekrQ7#J-W7#I}t5_40Jq;@Z6U^oSmzfh20Tp~FwXdQz#!`TT84AO#N=QA=e zcw|j)j_0@e%D~P1f`Ng7;i2w|Bq*IciP4rZXtEt+EMwv1IgHCUJ2I&-O-^UgVJzG{ zg=HgC{T=>)0_y~h3H%b&63h|YBBUjhB(zQFl`w;_lkhZ=3XxNyBBBkVKg7Jmwu$SB zFOp!9NRXH&aYm9)(nYdJa*Gt7)DP(}873Jq89f;{nHZTSnJqGBWIoBV$tua($p*=; zko_QMBUdALOkPbsPkxgEi$aIOGeslCD#de3MoLxnN+*<_C~GJ;DZf)`P+6w(Lsdi7 zPPIdIo9aC^3$-a~r__tomuQ%1OwzcdDWsX8S*OLI6{NLBn?qYc`+<&{PL<9HoqM_} zx-Gg_^mOzJ^tS0s>F+VnGiWl@Fbp!BW%$QP#7NI5!l=P$ozV?rHe(axCB_d-I85A3 z=9qGrdYIO)F%vM8F*7hbV)n&6%sk8djYXctHj6iwYL+IJewGE6^Q;uCQmhVGy|R|E zjf`=Rm-)`^^=>LTbx^t+k|?zbM9j9MIL@0Up$w1{_yhi zTH*D|Tf*DT`;?EBkB83;pEEwMd>wpye6RT_`BnHG@t5*1^FI_27jPorO`up{RA5iw zzQ9jGDnV&MbAo$am$`cwCx-Ilem{Hieup8lS;c?*=;giDGg+GZw~xGXyg*uqvsm*)p1_DVrLZ*)p1#n^`l8Dl4(G$uf$tv5Sg{ zi^(#Ih*>j=Dk_^A8<|6tn47ABHL0sXMy%uGtkZibnZ3@bOQjI`-AJ1I#sb+w=t8*vFAadDg0 zV0HD)_8M0fs+%&(f-@BZ0}F#BV>^`1${@ga7|Ld2kYwVRY;{UkJ&R!@12Y373nv2$ zgArpel+DVZz_<{~W@9j7yt29P6c;12A%n%_nWt?y4J~vnb&U*+j3>vP37D*Vrg8Gd yGYX7Bn=hSN#KgoDu({;C1&feqa(+sxYf5HGeo=gGYD#8l?&cMjWktA=vNZrpDPj@; delta 970 zcmX?ck@>=TW)lWR1_lORh6V;^h5$FW5Z?t(C4LMH&$lozFv$1^>l;PRNRwe;V2og3 zU`R;LO)Sv(ul;MHiBkRZH4F?49O*fgX|3A=+Zh-*Qy3Ty)MTV4rf3}NRbXIXxWmA} zV3v`Qn#lF@e=7q6qX+{7gGxqjNri&1pgRKt;|>M}rd2ul$%%3`}PW@{3DYk2+mq&}KNZfq_BlC)oLn z3=C@Wzl0S z*gS=0BUAkZ0SAEyfgXW90{;Zv1osGj5%LhK5t<})MOZ_GOC&~QnW&Ixm*^ca53vK{ zCgRh?KS^jv#7QiWxF*RW=_a{As!QsH^c3k8(g&n(NWYU2k#UoWlWCEeA+ts1g3J?H zE7>O5b8>ug5pp}^8RT8$rzuD&R45!#OJ$eJ z1yw0k57h$IQ)+5zG3qSpX6nZ@R5VgFmS}v^6wsWc`9#Z3YnIj~?IP_RIy^d2It4o0 zbj5Vjboc2o>3Qg_(f89oU?5_!%ixKjj$xnSGQ$IguZ)C@9E`GzW*8kaHZv|Vo@IQ) zM9*ZF$t_bm)A~iG>r4-sg_*UPy)gS@UT48z;bBo{vBTn+#RE$wO9RVImfx&`tZJ;* zS-r6?vF@|pXZ_8_z{brc&Sr;~I&?8|EVOe1}!ajs^g{y=+gqMYH34arj z6!9%mC{islB63!gvP@K0)QPBX(H=2sF$-cY#d^i=h?9wHiMtoC5MLC(CjMQ5-R2C= zyKa?zU2E#@MW(GzUP6ieR zBgOzIo0UO Date: Tue, 19 Sep 2023 13:46:53 +0200 Subject: [PATCH 048/130] QmlDesigner: Add tooltip on elided pills Change-Id: I4a40070b958a46e46009ba8f141fd5c286858ab7 Reviewed-by: Brook Cronin Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/connectionseditor/Pill.qml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml index d92163e9096..aa6cba03639 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml @@ -4,6 +4,7 @@ import QtQuick import StudioControls as StudioControls import StudioTheme as StudioTheme +import HelperWidgets as HelperWidgets FocusScope { id: root @@ -54,12 +55,25 @@ FocusScope { root.remove() } - MouseArea { + HelperWidgets.ToolTipArea { id: rootMouseArea anchors.fill: parent hoverEnabled: true cursorShape: root.isEditable() ? Qt.IBeamCursor : Qt.ArrowCursor onClicked: root.forceActiveFocus() + tooltip: { + if (textItem.visible) { + if (textItem.truncated) + return textItem.text + else + return "" + } + + if (textMetrics.width > textInput.width) + return textInput.text + + return "" + } } Rectangle { From ffe3670d900a94adcf56e2d6612f0e90ce60bf5d Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 19 Sep 2023 14:10:39 +0300 Subject: [PATCH 049/130] QmlDesigner: Implement changing effect maker preview image Fixes: QDS-10625 Change-Id: I48ef68fd4b64a37de3dc60f5decfcda1b43496dc Reviewed-by: Miikka Heikkinen Reviewed-by: Amr Elsayed Reviewed-by: Qt CI Patch Build Bot --- .../effectMakerQmlSources/EffectMaker.qml | 2 +- .../EffectMakerPreview.qml | 10 +- .../PreviewImagesComboBox.qml | 145 ++++++++++++++++++ .../effectMakerQmlSources/images/preview1.png | Bin 0 -> 23906 bytes .../effectMakerQmlSources/images/preview2.png | Bin 0 -> 23421 bytes .../effectMakerQmlSources/images/preview3.png | Bin 0 -> 10668 bytes .../effectMakerQmlSources/images/preview4.png | Bin 0 -> 11406 bytes .../effectMakerQmlSources/images/qt_logo.png | Bin 3663 -> 0 bytes 8 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/images/preview1.png create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/images/preview2.png create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/images/preview3.png create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/images/preview4.png delete mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/images/qt_logo.png diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 456539d13cf..49731b37130 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -25,7 +25,7 @@ Item { } EffectMakerPreview { - + mainRoot: root } Rectangle { diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index 10f6b516024..326c6017816 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -14,6 +14,8 @@ Column { width: parent.width + required property Item mainRoot + Rectangle { // toolbar width: parent.width height: StudioTheme.Values.toolbarHeight @@ -25,6 +27,12 @@ Column { anchors.rightMargin: 5 anchors.leftMargin: 5 + PreviewImagesComboBox { + id: imagesComboBox + + mainRoot: root.mainRoot + } + Item { Layout.fillWidth: true } @@ -114,7 +122,7 @@ Column { fillMode: Image.PreserveAspectFit smooth: true - source: "images/qt_logo.png" // TODO: update image + source: imagesComboBox.selectedImage Behavior on scale { NumberAnimation { diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml new file mode 100644 index 00000000000..68a588cdeff --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml @@ -0,0 +1,145 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuickDesignerTheme +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +StudioControls.ComboBox { + id: root + + actionIndicatorVisible: false + x: 5 + width: 60 + + model: [selectedImage] + + // hide default popup + popup.width: 0 + popup.height: 0 + + required property Item mainRoot + + property var images: ["images/preview1.png", "images/preview2.png", "images/preview3.png", "images/preview4.png"] + property string selectedImage: images[0] + + Connections { + target: root.popup + + function onAboutToShow() { + var a = mainRoot.mapToGlobal(0, 0) + var b = root.mapToItem(mainRoot, 0, 0) + + window.x = a.x + b.x + root.width - window.width + window.y = a.y + b.y + root.height - 1 + + window.show() + window.requestActivate() + } + + function onAboutToHide() { + window.hide() + } + } + + contentItem: Item { + width: 30 + height: 30 + + Image { + source: root.selectedImage + fillMode: Image.PreserveAspectFit + smooth: true + anchors.fill: parent + anchors.margins: 1 + } + + MouseArea { + anchors.fill: parent + onClicked: { + if (root.open) + root.popup.close(); + else + root.popup.open(); + } + } + } + + Window { + id: window + + width: col.width + 2 // 2: scrollView left and right 1px margins + height: Math.min(800, Math.min(col.height + 2, Screen.height - y - 40)) // 40: some bottom margin to cover OS bottom toolbar + flags: Qt.Popup | Qt.FramelessWindowHint + + onActiveChanged: { + if (!active && !root.hover) + root.popup.close() + } + + Rectangle { + anchors.fill: parent + color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeInteraction + border.width: 1 + + HelperWidgets.ScrollView { + anchors.fill: parent + anchors.margins: 1 + + Column { + id: col + + onWidthChanged: { + // Needed to update on first window showing, as row.width only gets + // correct value after the window is shown, so first showing is off + + var a = mainRoot.mapToGlobal(0, 0) + var b = root.mapToItem(mainRoot, 0, 0) + + window.x = a.x + b.x + root.width - col.width + } + + padding: 10 + spacing: 10 + + Repeater { + model: root.images + + Rectangle { + required property int index + required property var modelData + + color: "transparent" + border.color: root.selectedImage === modelData ? StudioTheme.Values.themeInteraction + : "transparent" + + width: 200 + height: 200 + + Image { + source: modelData + anchors.fill: parent + fillMode: Image.PreserveAspectFit + smooth: true + anchors.margins: 1 + } + + MouseArea { + anchors.fill: parent + + onClicked: { + root.selectedImage = root.images[index] + root.popup.close() + } + } + } + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/images/preview1.png b/share/qtcreator/qmldesigner/effectMakerQmlSources/images/preview1.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a58e8e57d2b59beb26575d38c1fa6e70d5dc79 GIT binary patch literal 23906 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 Nv!|<{%Q~loCIFU-xCsCN literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/images/preview3.png b/share/qtcreator/qmldesigner/effectMakerQmlSources/images/preview3.png new file mode 100644 index 0000000000000000000000000000000000000000..3c964b8d4dd9f289e88bccc85730af5ae2326a98 GIT binary patch literal 10668 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 Y{dEuaux|2aU|?YIboFyt=akR{06!X|X#fBK literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/images/qt_logo.png b/share/qtcreator/qmldesigner/effectMakerQmlSources/images/qt_logo.png deleted file mode 100644 index 5181f1b5ab43f8536b1c828b2a470a0036389a72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3663 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 From f2343b417e7fe22ab6d78365f7ccb49b773771be Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 19 Sep 2023 18:17:01 +0200 Subject: [PATCH 050/130] QmlDesigner: Close the popup when model is detached This happens when leaving the edit mode or the document is changed. Task-number: QDS-10707 Change-Id: I4b4e2511674d38c2d733db29070c01252c27c386 Reviewed-by: Aleksei German --- .../qmldesigner/connectionseditor/ConnectionsDialog.qml | 2 +- .../components/connectioneditor/connectionmodel.cpp | 7 ++++++- .../components/connectioneditor/connectionmodel.h | 3 ++- .../components/connectioneditor/connectionview.cpp | 1 + 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml index 131ae6cee99..53eaad0adf4 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml @@ -43,7 +43,7 @@ PopupDialog { Connections { target: root.backend - function onPopupTargetRemoved() { + function onPopupShouldClose() { root.close() } } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index c0b819371a8..54965013a44 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -493,7 +493,7 @@ void ConnectionModel::nodeAboutToBeRemoved(const ModelNode &removedNode) if (selectedSignal.isValid()) { ModelNode targetNode = getTargetNodeForConnection(selectedSignal.parentModelNode()); if (targetNode == removedNode) { - emit m_delegate->popupTargetRemoved(); + emit m_delegate->popupShouldClose(); } } } @@ -2103,4 +2103,9 @@ ConnectionEditorStatements::ComparativeStatement ConditionListModel::toStatement return {}; } +void QmlDesigner::ConnectionModel::modelAboutToBeDetached() +{ + emit m_delegate->popupShouldClose(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index d9b13cb22d2..d25d5aa2d3b 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -72,6 +72,7 @@ public: void selectProperty(const SignalHandlerProperty &property); void nodeAboutToBeRemoved(const ModelNode &removedNode); + void modelAboutToBeDetached(); signals: void currentIndexChanged(); @@ -298,7 +299,7 @@ signals: void hasConditionChanged(); void hasElseChanged(); void sourceChanged(); - void popupTargetRemoved(); + void popupShouldClose(); private: int currentRow() const; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 0b02ee0e34b..b96afd69665 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -169,6 +169,7 @@ void ConnectionView::modelAboutToBeDetached(Model *model) bindingModel()->reset(); dynamicPropertiesModel()->reset(); connectionModel()->resetModel(); + connectionModel()->modelAboutToBeDetached(); } void ConnectionView::nodeCreated(const ModelNode & /*createdNode*/) From 949612c23e1c11b86628994494832cf0acc6ab4e Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Tue, 19 Sep 2023 17:37:06 +0200 Subject: [PATCH 051/130] QmlJsEditor: Fix crash on mode switch after save Task-number: QDS-10709 Change-Id: I0eceef3815b6358d88329828d199d5918a273edf Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmljseditor/qmljsoutline.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmljseditor/qmljsoutline.cpp b/src/plugins/qmljseditor/qmljsoutline.cpp index a987120731d..75e0b0fdc22 100644 --- a/src/plugins/qmljseditor/qmljsoutline.cpp +++ b/src/plugins/qmljseditor/qmljsoutline.cpp @@ -145,10 +145,15 @@ void QmlJSOutlineWidget::setEditor(QmlJSEditorWidget *editor) connect(m_editor, &QmlJSEditorWidget::outlineModelIndexChanged, this, &QmlJSOutlineWidget::updateSelectionInTree); - connect(m_editor->qmlJsEditorDocument()->outlineModel(), &QmlOutlineModel::updated, this, [this] () { - m_treeView->expandAll(); - m_editor->updateOutlineIndexNow(); - }); + connect(m_editor->qmlJsEditorDocument()->outlineModel(), + &QmlOutlineModel::updated, + this, + [treeView = QPointer(m_treeView), editor = QPointer(m_editor)]() { + if (treeView) + treeView->expandAll(); + if (editor) + editor->updateOutlineIndexNow(); + }); } QList QmlJSOutlineWidget::filterMenuActions() const From ff02efe5413edf5f756e7b2a7b0340842bba0b06 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 19 Sep 2023 17:33:10 +0300 Subject: [PATCH 052/130] QmlDesigner: Fix the crash on visiting callExpression Fixed the bug for visiting a callExpression outside of the statement. Task-number: QDS-10708 Change-Id: Iebe8c1c8d73f1d601e1fba6adaca1ce89d89d0ff Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Aleksei German --- .../components/connectioneditor/connectioneditorevaluator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp index c7d3ae977a5..5bb96390253 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp @@ -967,11 +967,11 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::FieldMemberExpression *fieldEx bool ConnectionEditorEvaluator::visit(QmlJS::AST::CallExpression *callExpression) { if (d->isInIfCondition()) - d->checkValidityAndReturn(false, "Functions are not allowd in the expressions"); + return d->checkValidityAndReturn(false, "Functions are not allowd in the expressions"); MatchedStatement *currentStatement = d->currentStatement(); if (!currentStatement) - d->checkValidityAndReturn(false, "Invalid place to call an expression"); + return d->checkValidityAndReturn(false, "Invalid place to call an expression"); if (ConnectionEditorStatements::isEmptyStatement(*currentStatement)) { if (d->parentNodeStatus().childId() == 0) { From e4c96428c73f7e15b2bae924b2557b48983b9270 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Mon, 18 Sep 2023 11:21:24 +0200 Subject: [PATCH 053/130] QmlDesigner: Update Tooltips for Connection view titles This patch update tooltip texts for titles directly under the connection view. Fixes: QDS-10626 Change-Id: If0eb948143536702df0219e64db8a7370ba8cb42 Reviewed-by: Thomas Hartmann Reviewed-by: Mats Honkamaa Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- share/qtcreator/qmldesigner/connectionseditor/Main.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/Main.qml b/share/qtcreator/qmldesigner/connectionseditor/Main.qml index 6343e546c73..c84f6dcdcbb 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Main.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Main.qml @@ -56,7 +56,7 @@ Rectangle { id: connections buttonIcon: StudioTheme.Constants.connections_medium text: qsTr("Connections") - tooltip: qsTr("This is a tooltip.") + tooltip: qsTr("Sets logical connection between the components and the signals.") checked: true autoExclusive: true checkable: true @@ -66,7 +66,7 @@ Rectangle { id: bindings buttonIcon: StudioTheme.Constants.binding_medium text: qsTr("Bindings") - tooltip: qsTr("This is a tooltip.") + tooltip: qsTr("Sets the relation between the properties of two components to bind them together.") autoExclusive: true checkable: true } @@ -75,7 +75,7 @@ Rectangle { id: properties buttonIcon: StudioTheme.Constants.properties_medium text: qsTr("Properties") - tooltip: qsTr("This is a tooltip.") + tooltip: qsTr("Sets an additional property for the component.") autoExclusive: true checkable: true } @@ -91,7 +91,7 @@ Rectangle { id: addButton style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.add_medium - tooltip: qsTr("Add something.") + tooltip: qsTr("Adds a Connection, Binding, or Custom Property to the components.") onClicked: { if (connections.checked) connectionsListView.addConnection() From 4ff83dd4cc9634f9217cc91ee443e1b6761315b9 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 20 Sep 2023 12:53:56 +0200 Subject: [PATCH 054/130] QmlDesigner: Show remove button on hover/selection Change-Id: Ibb58d2f60bea0bf780bd51d209a5ddd953e3cf64 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: Brook Cronin Reviewed-by: --- .../connectionseditor/ConnectionsListView.qml | 10 +++++++--- .../imports/StudioTheme/ControlStyle.qml | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml index a8159056fc0..a9075c59b1b 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml @@ -104,9 +104,11 @@ ListView { property alias delegateMouseArea: mouseArea + property bool hovered: mouseArea.containsMouse || toolTipArea.containsMouse + width: ListView.view.width height: root.style.squareControlSize.height - color: mouseArea.containsMouse ? + color: itemDelegate.hovered ? itemDelegate.ListView.isCurrentItem ? root.style.interactionHover : root.style.background.hover : "transparent" @@ -170,10 +172,12 @@ ListView { height: root.style.squareControlSize.height color: toolTipArea.containsMouse ? - itemDelegate.ListView.isCurrentItem ? root.style.interactionHover - : root.style.background.hover + itemDelegate.ListView.isCurrentItem ? root.style.interactionGlobalHover + : root.style.background.globalHover : "transparent" + visible: itemDelegate.hovered || itemDelegate.ListView.isCurrentItem + Text { anchors.fill: parent diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml index 460ff6db3f3..60ca3148fc5 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml @@ -76,6 +76,7 @@ QtObject { // Special colors property color interaction: Values.themeInteraction property color interactionHover: Values.themeInteractionHover + property color interactionGlobalHover: "#ffB0E1FC" // TODO needs to removed in the future property color thumbnailLabelBackground: Values.themeThumbnailLabelBackground From 5676e612982d3c87f403677bcb63e0b6343f9b7d Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 19 Sep 2023 17:14:24 +0200 Subject: [PATCH 055/130] QmlDesigner: Add transient scroll bar to ComboBox Change-Id: I118e4801fef9fab117e4d7355e56616c7d001858 Reviewed-by: Thomas Hartmann Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Brook Cronin --- .../assetsLibraryQmlSources/AssetsView.qml | 4 +- .../connectionseditor/BindingsListView.qml | 7 +- .../connectionseditor/ConnectionsListView.qml | 7 +- .../connectionseditor/PropertiesListView.qml | 7 +- .../connectionseditor/SuggestionPopup.qml | 4 +- .../imports/NewProjectDialog/Details.qml | 46 ++++++++----- .../imports/NewProjectDialog/PresetView.qml | 13 ++-- .../imports/NewProjectDialog/Styles.qml | 19 ++++-- .../imports/HelperWidgets/ScrollView.qml | 8 ++- .../imports/HelperWidgets/qmldir | 1 - .../imports/StudioControls/ComboBox.qml | 63 +++++++++-------- .../StudioControls/TopLevelComboBox.qml | 67 ++++++++++--------- .../TransientScrollBar.qml} | 3 +- .../StudioControls/VerticalScrollBar.qml | 38 ----------- .../imports/StudioControls/qmldir | 2 +- .../ConnectionPopupControlStyle.qml | 10 ++- .../imports/StudioTheme/ControlStyle.qml | 3 +- .../imports/StudioTheme/Values.qml | 7 +- .../imports/StudioTheme/ViewStyle.qml | 9 +++ .../imports/StudioTheme/qmldir | 1 + .../qmldesigner/stateseditor/Main.qml | 6 +- 21 files changed, 173 insertions(+), 152 deletions(-) rename share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/{HelperWidgets/ScrollBar.qml => StudioControls/TransientScrollBar.qml} (94%) delete mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/VerticalScrollBar.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ViewStyle.qml diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml index 98b85d85c96..9326e6a5e35 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml @@ -5,6 +5,7 @@ import QtQuick import QtQuick.Controls import HelperWidgets as HelperWidgets import StudioControls as StudioControls +import StudioTheme as StudioTheme import AssetsLibraryBackend TreeView { @@ -53,8 +54,9 @@ TreeView { HoverHandler { id: hoverHandler } - ScrollBar.vertical: HelperWidgets.ScrollBar { + ScrollBar.vertical: StudioControls.TransientScrollBar { id: verticalScrollBar + style: StudioTheme.Values.viewStyle parent: root x: root.width - verticalScrollBar.width y: 0 diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml index 83b2322e37b..acebb0e0bf9 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml @@ -3,8 +3,8 @@ import QtQuick import QtQuick.Controls -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls import StudioTheme as StudioTheme import ConnectionsEditorEditorBackend @@ -30,8 +30,9 @@ ListView { HoverHandler { id: hoverHandler } - ScrollBar.vertical: HelperWidgets.ScrollBar { + ScrollBar.vertical: StudioControls.TransientScrollBar { id: verticalScrollBar + style: StudioTheme.Values.viewStyle parent: root x: root.width - verticalScrollBar.width y: 0 diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml index a9075c59b1b..1827b4f8f43 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml @@ -3,8 +3,8 @@ import QtQuick import QtQuick.Controls -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls import StudioTheme as StudioTheme import ConnectionsEditorEditorBackend @@ -30,8 +30,9 @@ ListView { HoverHandler { id: hoverHandler } - ScrollBar.vertical: HelperWidgets.ScrollBar { + ScrollBar.vertical: StudioControls.TransientScrollBar { id: verticalScrollBar + style: StudioTheme.Values.viewStyle parent: root x: root.width - verticalScrollBar.width y: 0 diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index c6fdf333333..dc8baf9bdd7 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -3,8 +3,8 @@ import QtQuick import QtQuick.Controls -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls import StudioTheme as StudioTheme import ConnectionsEditorEditorBackend @@ -30,8 +30,9 @@ ListView { HoverHandler { id: hoverHandler } - ScrollBar.vertical: HelperWidgets.ScrollBar { + ScrollBar.vertical: StudioControls.TransientScrollBar { id: verticalScrollBar + style: StudioTheme.Values.viewStyle parent: root x: root.width - verticalScrollBar.width y: 0 diff --git a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml index 332059fbc6c..fad01c32c4d 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml @@ -135,7 +135,7 @@ Controls.Popup { boundsMovement: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds - Controls.ScrollBar.vertical: HelperWidgets.ScrollBar { + Controls.ScrollBar.vertical: StudioControls.TransientScrollBar { id: listScrollBar parent: listView x: listView.width - listScrollBar.width @@ -195,7 +195,7 @@ Controls.Popup { boundsMovement: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds - Controls.ScrollBar.vertical: HelperWidgets.ScrollBar { + Controls.ScrollBar.vertical: StudioControls.TransientScrollBar { id: treeScrollBar parent: treeView x: treeView.width - treeScrollBar.width diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml index ef8e020a5d7..d85aab300d7 100644 --- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml +++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml @@ -6,7 +6,7 @@ import QtQuick.Controls import QtQuick import QtQuick.Layouts -import StudioControls as SC +import StudioControls as StudioControls import StudioTheme as StudioTheme import BackendApi @@ -22,8 +22,8 @@ Item { anchors.fill: parent Item { - x: DialogValues.detailsPanePadding // left padding - width: parent.width - DialogValues.detailsPanePadding * 2 // right padding + x: DialogValues.detailsPanePadding * 2 // left padding + width: parent.width - DialogValues.detailsPanePadding * 3 // right padding height: parent.height Column { @@ -44,6 +44,7 @@ Item { } Flickable { + id: flickable width: parent.width height: parent.height - detailsHeading.height - DialogValues.defaultPadding - savePresetButton.height @@ -52,14 +53,27 @@ Item { boundsBehavior: Flickable.StopAtBounds clip: true - ScrollBar.vertical: SC.VerticalScrollBar {} + HoverHandler { id: hoverHandler } + + ScrollBar.vertical: StudioControls.TransientScrollBar { + id: verticalScrollBar + style: StudioTheme.Values.viewStyle + parent: flickable + x: flickable.width - verticalScrollBar.width + y: 0 + height: flickable.availableHeight + orientation: Qt.Vertical + + show: (hoverHandler.hovered || flickable.focus || verticalScrollBar.inUse) + && verticalScrollBar.isNeeded + } Column { id: scrollContent width: parent.width - DialogValues.detailsPanePadding spacing: DialogValues.defaultPadding - SC.TextField { + StudioControls.TextField { id: projectNameTextField actionIndicatorVisible: false translationIndicatorVisible: false @@ -85,7 +99,7 @@ Item { RowLayout { // Project location width: parent.width - SC.TextField { + StudioControls.TextField { Layout.fillWidth: true id: projectLocationTextField actionIndicatorVisible: false @@ -102,7 +116,7 @@ Item { value: projectLocationTextField.text } - SC.AbstractButton { + StudioControls.AbstractButton { implicitWidth: 30 iconSize: 20 visible: true @@ -114,7 +128,7 @@ Item { if (newLocation) projectLocationTextField.text = newLocation } - } // SC.AbstractButton + } } // Project location RowLayout Item { width: parent.width; height: DialogValues.narrowSpacing(7) } @@ -171,7 +185,7 @@ Item { } // Text } // RowLayout - SC.CheckBox { + StudioControls.CheckBox { id: defaultLocationCheckbox actionIndicatorVisible: false text: qsTr("Use as default project location") @@ -187,7 +201,7 @@ Item { Rectangle { width: parent.width; height: 1; color: DialogValues.dividerlineColor } - SC.ComboBox { // Screen Size ComboBox + StudioControls.ComboBox { // Screen Size ComboBox id: screenSizeComboBox actionIndicatorVisible: false currentIndex: -1 @@ -253,7 +267,7 @@ Item { } // content items - SC.RealSpinBox { + StudioControls.RealSpinBox { id: widthField actionIndicatorVisible: false implicitWidth: 70 @@ -274,7 +288,7 @@ Item { value: widthField.realValue } - SC.RealSpinBox { + StudioControls.RealSpinBox { id: heightField actionIndicatorVisible: false implicitWidth: 70 @@ -368,7 +382,7 @@ Item { color: DialogValues.dividerlineColor } - SC.CheckBox { + StudioControls.CheckBox { id: useQtVirtualKeyboard actionIndicatorVisible: false text: qsTr("Use Qt Virtual Keyboard") @@ -389,7 +403,7 @@ Item { color: DialogValues.textColor } - SC.ComboBox { // Target Qt Version ComboBox + StudioControls.ComboBox { // Target Qt Version ComboBox id: qtVersionComboBox actionIndicatorVisible: false implicitWidth: 82 @@ -421,7 +435,7 @@ Item { } // ScrollView } // Column - SC.AbstractButton { + StudioControls.AbstractButton { id: savePresetButton width: StudioTheme.Values.singleControlColumnWidth buttonIcon: qsTr("Save Custom Preset") @@ -459,7 +473,7 @@ Item { color: DialogValues.textColor } - SC.TextField { + StudioControls.TextField { id: presetNameTextField actionIndicatorVisible: false translationIndicatorVisible: false diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PresetView.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PresetView.qml index c0903bc03b8..e72cee1840f 100644 --- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PresetView.qml +++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PresetView.qml @@ -6,7 +6,7 @@ import QtQuick.Controls import QtQuick import QtQuick.Layouts -import StudioControls as SC +import StudioControls as StudioControls import StudioTheme as StudioTheme import BackendApi @@ -23,12 +23,17 @@ ScrollView { property bool selectLast: false ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical: SC.VerticalScrollBar { + ScrollBar.vertical: StudioControls.TransientScrollBar { + id: verticalScrollBar + style: StudioTheme.Values.viewStyle parent: scrollView - x: scrollView.width + (DialogValues.gridMargins - - StudioTheme.Values.scrollBarThickness) * 0.5 + x: scrollView.width + (DialogValues.gridMargins - verticalScrollBar.width) * 0.5 y: scrollView.topPadding height: scrollView.availableHeight + orientation: Qt.Vertical + + show: (scrollView.hovered || scrollView.focus || verticalScrollBar.inUse) + && verticalScrollBar.isNeeded } contentWidth: gridView.contentItem.childrenRect.width diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Styles.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Styles.qml index d89d2d5157e..b87add5c510 100644 --- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Styles.qml +++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Styles.qml @@ -6,7 +6,7 @@ import QtQuick.Window import QtQuick.Controls import QtQuick.Layouts -import StudioControls as SC +import StudioControls as StudioControls import StudioTheme as StudioTheme import BackendApi @@ -59,7 +59,7 @@ Item { } } - SC.ComboBox { // Style Filter ComboBox + StudioControls.ComboBox { // Style Filter ComboBox id: styleComboBox actionIndicatorVisible: false currentIndex: 0 @@ -93,10 +93,17 @@ Item { width: parent.width ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical: SC.VerticalScrollBar { - id: styleScrollBar - x: stylesList.width + (DialogValues.stylesPanePadding - - StudioTheme.Values.scrollBarThickness) * 0.5 + ScrollBar.vertical: StudioControls.TransientScrollBar { + id: verticalScrollBar + style: StudioTheme.Values.viewStyle + parent: scrollView + x: scrollView.width + (DialogValues.gridMargins - verticalScrollBar.width) * 0.5 + y: scrollView.topPadding + height: scrollView.availableHeight + orientation: Qt.Vertical + + show: (scrollView.hovered || scrollView.focus || verticalScrollBar.inUse) + && verticalScrollBar.isNeeded } ListView { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml index f33d0dd35bc..34342958f79 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick -//import QtQuick.Controls as C +import StudioControls as StudioControls import StudioTheme 1.0 as StudioTheme Flickable { @@ -26,8 +26,9 @@ Flickable { HoverHandler { id: hoverHandler } - ScrollBar.horizontal: ScrollBar { + ScrollBar.horizontal: StudioControls.TransientScrollBar { id: horizontalScrollBar + style: StudioTheme.Values.viewStyle parent: flickable x: 0 y: flickable.height - horizontalScrollBar.height @@ -40,8 +41,9 @@ Flickable { otherInUse: verticalScrollBar.inUse } - ScrollBar.vertical: ScrollBar { + ScrollBar.vertical: StudioControls.TransientScrollBar { id: verticalScrollBar + style: StudioTheme.Values.viewStyle parent: flickable x: flickable.width - verticalScrollBar.width y: 0 diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir index 1e475a665c2..9c19a45e2ef 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir @@ -59,7 +59,6 @@ PropertyEditorPane 2.0 PropertyEditorPane.qml PropertyLabel 2.0 PropertyLabel.qml PaddingSection 2.0 PaddingSection.qml RoundedPanel 2.0 RoundedPanel.qml -ScrollBar 2.0 ScrollBar.qml ScrollView 2.0 ScrollView.qml SecondColumnLayout 2.0 SecondColumnLayout.qml Section 2.0 Section.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml index abbb32744b5..0c68d5195bd 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml @@ -130,14 +130,13 @@ T.ComboBox { id: itemDelegate width: comboBoxPopup.width - comboBoxPopup.leftPadding - comboBoxPopup.rightPadding - - (comboBoxPopupScrollBar.visible ? comboBoxPopupScrollBar.contentItem.implicitWidth - + 2 : 0) // TODO Magic number height: control.style.controlSize.height - 2 * control.style.borderWidth padding: 0 enabled: model.enabled === undefined ? true : model.enabled contentItem: Text { - leftPadding: itemDelegateIconArea.width + leftPadding: 8 + rightPadding: verticalScrollBar.style.scrollBarThicknessHover text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) @@ -146,34 +145,16 @@ T.ComboBox { if (!itemDelegate.enabled) return control.style.text.disabled - return itemDelegate.highlighted ? control.style.text.selectedText - : control.style.text.idle + if (control.currentIndex === index) + return control.style.text.selectedText + + return control.style.text.idle } font: control.font elide: Text.ElideRight verticalAlignment: Text.AlignVCenter } - Item { - id: itemDelegateIconArea - width: itemDelegate.height - height: itemDelegate.height - - T.Label { - id: itemDelegateIcon - text: StudioTheme.Constants.tickIcon - color: itemDelegate.highlighted ? control.style.text.selectedText - : control.style.text.idle - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: control.style.smallIconFontSize - visible: control.currentIndex === index - anchors.fill: parent - renderType: Text.NativeRendering - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - } - highlighted: control.highlightedIndex === index background: Rectangle { @@ -182,7 +163,21 @@ T.ComboBox { y: 0 width: itemDelegate.width height: itemDelegate.height - color: itemDelegate.highlighted ? control.style.interaction : "transparent" + color: { + if (!itemDelegate.enabled) + return "transparent" + + if (itemDelegate.hovered && control.currentIndex === index) + return control.style.interactionHover + + if (control.currentIndex === index) + return control.style.interaction + + if (itemDelegate.hovered) + return control.style.background.hover + + return "transparent" + } } } @@ -211,9 +206,19 @@ T.ComboBox { model: control.popup.visible ? control.delegateModel : null currentIndex: control.highlightedIndex boundsBehavior: Flickable.StopAtBounds - ScrollBar.vertical: ScrollBar { - id: comboBoxPopupScrollBar - visible: listView.height < listView.contentHeight + + HoverHandler { id: hoverHandler } + + ScrollBar.vertical: TransientScrollBar { + id: verticalScrollBar + parent: listView + x: listView.width - verticalScrollBar.width + y: 0 + height: listView.availableHeight + orientation: Qt.Vertical + + show: (hoverHandler.hovered || verticalScrollBar.inUse) + && verticalScrollBar.isNeeded } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml index eb1a6c631d2..ea80abe7ea3 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml @@ -138,9 +138,19 @@ T.ComboBox { currentIndex: control.highlightedIndex boundsBehavior: Flickable.StopAtBounds - ScrollBar.vertical: ScrollBar { - id: comboBoxPopupScrollBar - visible: listView.height < listView.contentHeight + HoverHandler { id: hoverHandler } + + ScrollBar.vertical: TransientScrollBar { + id: verticalScrollBar + style: control.style + parent: listView + x: listView.width - verticalScrollBar.width + y: 0 + height: listView.availableHeight + orientation: Qt.Vertical + + show: (hoverHandler.hovered || verticalScrollBar.inUse) + && verticalScrollBar.isNeeded } delegate: ItemDelegate { @@ -158,7 +168,8 @@ T.ComboBox { enabled: model.enabled === undefined ? true : model.enabled contentItem: Text { - leftPadding: itemDelegateIconArea.width + leftPadding: 8 + rightPadding: verticalScrollBar.style.scrollBarThicknessHover text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) @@ -167,47 +178,37 @@ T.ComboBox { if (!itemDelegate.enabled) return control.style.text.disabled - return itemDelegate.hovered ? control.style.text.selectedText - : control.style.text.idle + if (control.currentIndex === index) + return control.style.text.selectedText + + return control.style.text.idle } font: control.font elide: Text.ElideRight verticalAlignment: Text.AlignVCenter } - Item { - id: itemDelegateIconArea - width: itemDelegate.height - height: itemDelegate.height - - T.Label { - id: itemDelegateIcon - text: StudioTheme.Constants.tickIcon - color: { - if (!itemDelegate.enabled) - return control.style.text.disabled - - return itemDelegate.hovered ? control.style.text.selectedText - : control.style.text.idle - } - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: control.style.smallIconFontSize - visible: control.currentIndex === index - anchors.fill: parent - renderType: Text.NativeRendering - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - } - background: Rectangle { id: itemDelegateBackground x: control.style.borderWidth y: 0 width: itemDelegate.width - 2 * control.style.borderWidth height: itemDelegate.height - color: itemDelegate.hovered && itemDelegate.enabled ? control.style.interaction - : "transparent" + color: { + if (!itemDelegate.enabled) + return "transparent" + + if (itemDelegate.hovered && control.currentIndex === index) + return control.style.interactionHover + + if (control.currentIndex === index) + return control.style.interaction + + if (itemDelegate.hovered) + return control.style.background.hover + + return "transparent" + } } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollBar.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TransientScrollBar.qml similarity index 94% rename from share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollBar.qml rename to share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TransientScrollBar.qml index 43456d46ddd..141a981f749 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollBar.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TransientScrollBar.qml @@ -14,7 +14,8 @@ T.ScrollBar { property bool otherInUse: false property bool isNeeded: control.size < 1.0 property bool inUse: control.hovered || control.pressed - property int thickness: control.inUse || control.otherInUse ? 10 : 8 + property int thickness: control.inUse || control.otherInUse ? control.style.scrollBarThicknessHover + : control.style.scrollBarThickness property bool scrollBarVisible: parent.childrenRect.height > parent.height diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/VerticalScrollBar.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/VerticalScrollBar.qml deleted file mode 100644 index 752f2bc6e4e..00000000000 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/VerticalScrollBar.qml +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick -//import QtQuick.Controls -import StudioTheme 1.0 as StudioTheme - -ScrollBar { - id: control - - implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, - implicitContentWidth + leftPadding + rightPadding) - implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, - implicitContentHeight + topPadding + bottomPadding) - - property bool scrollBarVisible: parent.contentHeight > control.height - - minimumSize: control.width / control.height - orientation: Qt.Vertical - policy: control.scrollBarVisible ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff - - height: parent.availableHeight - - (parent.bothVisible ? parent.horizontalThickness : 0) - padding: control.active ? control.style.scrollBarActivePadding - : control.style.scrollBarInactivePadding - - background: Rectangle { - implicitWidth: control.style.scrollBarThickness - implicitHeight: control.style.scrollBarThickness - color: control.style.scrollBar.track - } - - contentItem: Rectangle { - implicitWidth: control.style.scrollBarThickness - 2 * control.padding - implicitHeight: control.style.scrollBarThickness - 2 * control.padding - color: control.style.scrollBar.handle - } -} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir index d6071b6ff2d..5ce433812c2 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir @@ -47,6 +47,6 @@ TabButton 1.0 TabButton.qml TextArea 1.0 TextArea.qml TextField 1.0 TextField.qml ToolTip 1.0 ToolTip.qml +TransientScrollBar 1.0 TransientScrollBar.qml TranslationIndicator 1.0 TranslationIndicator.qml -VerticalScrollBar 1.0 VerticalScrollBar.qml TopLevelComboBox 1.0 TopLevelComboBox.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupControlStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupControlStyle.qml index 9a4d1194c67..828e52e93ce 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupControlStyle.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupControlStyle.qml @@ -4,13 +4,15 @@ import QtQuick ControlStyle { - radius: Values.smallRadius baseIconFontSize: Values.baseFont controlSize: Qt.size(Values.viewBarComboWidth, Values.viewBarComboHeight) smallIconFontSize: Values.baseFont + scrollBarThickness: 4 + scrollBarThicknessHover: 6 + background: ControlStyle.BackgroundColors { idle: Values.themePopoutControlBackground_idle hover: Values.themePopoutControlBackground_hover @@ -36,4 +38,10 @@ ControlStyle { interaction: Values.themeInteraction disabled: Values.themePopoutControlBorder_disabled } + + scrollBar: ControlStyle.ScrollBarColors { + track: Values.themeScrollBarTrack + handle: Values.themeScrollBarHandle_idle + handleHover: Values.themeScrollBarHandle + } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml index 60ca3148fc5..b0e6e3e798e 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml @@ -67,7 +67,8 @@ QtObject { property real sectionHeadHeight: Values.sectionHeadHeight property real sectionHeadSpacerHeight: Values.sectionHeadSpacerHeight - property real scrollBarThickness: Values.scrollBarThickness + property real scrollBarThickness: 4//Values.scrollBarThickness + property real scrollBarThicknessHover: 6//Values.scrollBarThicknessHover property real scrollBarActivePadding: Values.scrollBarActivePadding property real scrollBarInactivePadding: Values.scrollBarInactivePadding diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 31b9fc71e85..0aa9a1b7217 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -99,7 +99,8 @@ QtObject { property real inputHorizontalPadding: Math.round(6 * values.scaleFactor) property real typeLabelVerticalShift: Math.round(6 * values.scaleFactor) - property real scrollBarThickness: 10 + property real scrollBarThickness: 8 + property real scrollBarThicknessHover: 10 property real scrollBarActivePadding: 1 property real scrollBarInactivePadding: 2 @@ -458,9 +459,6 @@ QtObject { property color themePillTextSelected: Theme.color(Theme.DSpillTextSelected) property color themePillTextEdit: Theme.color(Theme.DspillTextEdit) - - - // Control Style Mapping property ControlStyle controlStyle: DefaultStyle {} property ControlStyle connectionPopupControlStyle: ConnectionPopupControlStyle {} @@ -474,4 +472,5 @@ QtObject { property ControlStyle statusbarControlStyle: StatusBarControlStyle {} property ControlStyle statesControlStyle: StatesControlStyle {} property ControlStyle searchControlStyle: SearchControlStyle {} + property ControlStyle viewStyle: ViewStyle {} } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ViewStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ViewStyle.qml new file mode 100644 index 00000000000..2502f0a2523 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ViewStyle.qml @@ -0,0 +1,9 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick + +ControlStyle { + scrollBarThickness: Values.scrollBarThickness + scrollBarThicknessHover: Values.scrollBarThicknessHover +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir index 60138d86d0f..45e55c4edc6 100755 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir @@ -14,3 +14,4 @@ StatusBarControlStyle 1.0 StatusBarControlStyle.qml TopToolbarButtonStyle 1.0 TopToolbarButtonStyle.qml ViewBarButtonStyle 1.0 ViewBarButtonStyle.qml ViewBarControlStyle 1.0 ViewBarControlStyle.qml +ViewStyle 1.0 ViewStyle.qml diff --git a/share/qtcreator/qmldesigner/stateseditor/Main.qml b/share/qtcreator/qmldesigner/stateseditor/Main.qml index 7e8ad669327..7126a327eaf 100644 --- a/share/qtcreator/qmldesigner/stateseditor/Main.qml +++ b/share/qtcreator/qmldesigner/stateseditor/Main.qml @@ -579,8 +579,9 @@ Rectangle { anchors.topMargin: root.topMargin anchors.leftMargin: root.leftMargin - ScrollBar.horizontal: HelperWidgets.ScrollBar { + ScrollBar.horizontal: StudioControls.TransientScrollBar { id: horizontalBar + style: StudioTheme.Values.viewStyle parent: scrollView x: scrollView.leftPadding y: scrollView.height - height @@ -592,8 +593,9 @@ Rectangle { otherInUse: verticalBar.inUse } - ScrollBar.vertical: HelperWidgets.ScrollBar { + ScrollBar.vertical: StudioControls.TransientScrollBar { id: verticalBar + style: StudioTheme.Values.viewStyle parent: scrollView x: scrollView.mirrored ? 0 : scrollView.width - width y: scrollView.topPadding From 3f1baa751918e9add594a552253e51bb106ad723 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 20 Sep 2023 16:49:06 +0300 Subject: [PATCH 056/130] QmlDesigner: Update effect maker preview images Also fix clicking outside the comboboxes doesn't close them. Change-Id: I461501f90756f4e4eb96dace3839e9b6f0c0d9b8 Reviewed-by: Miikka Heikkinen --- .../EffectNodesComboBox.qml | 20 ++++++++--------- .../PreviewImagesComboBox.qml | 21 +++++++----------- .../effectMakerQmlSources/images/preview0.png | Bin 0 -> 22504 bytes 3 files changed, 18 insertions(+), 23 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/images/preview0.png diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml index 9c52f687aac..6d1b1cec960 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml @@ -30,27 +30,27 @@ StudioControls.ComboBox { var a = mainRoot.mapToGlobal(0, 0) var b = root.mapToItem(mainRoot, 0, 0) - effectNodesWindow.x = a.x + b.x + root.width - effectNodesWindow.width - effectNodesWindow.y = a.y + b.y + root.height - 1 + window.x = a.x + b.x + root.width - window.width + window.y = a.y + b.y + root.height - 1 - effectNodesWindow.show() - effectNodesWindow.requestActivate() + window.show() + window.requestActivate() } function onAboutToHide() { - effectNodesWindow.hide() + window.hide() } } Window { - id: effectNodesWindow + id: window width: row.width + 2 // 2: scrollView left and right 1px margins height: Math.min(800, Math.min(row.height + 2, Screen.height - y - 40)) // 40: some bottom margin to cover OS bottom toolbar - flags: Qt.Popup | Qt.FramelessWindowHint + flags: Qt.Popup | Qt.FramelessWindowHint - onActiveChanged: { - if (!active && !root.hover) + onActiveFocusItemChanged: { + if (!window.activeFocusItem && !root.indicator.hover && root.popup.opened) root.popup.close() } @@ -74,7 +74,7 @@ StudioControls.ComboBox { var a = mainRoot.mapToGlobal(0, 0) var b = root.mapToItem(mainRoot, 0, 0) - effectNodesWindow.x = a.x + b.x + root.width - row.width + window.x = a.x + b.x + root.width - row.width } padding: 10 diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml index 68a588cdeff..445935b532e 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml @@ -23,7 +23,11 @@ StudioControls.ComboBox { required property Item mainRoot - property var images: ["images/preview1.png", "images/preview2.png", "images/preview3.png", "images/preview4.png"] + property var images: ["images/preview0.png", + "images/preview1.png", + "images/preview2.png", + "images/preview3.png", + "images/preview4.png"] property string selectedImage: images[0] Connections { @@ -56,16 +60,6 @@ StudioControls.ComboBox { anchors.fill: parent anchors.margins: 1 } - - MouseArea { - anchors.fill: parent - onClicked: { - if (root.open) - root.popup.close(); - else - root.popup.open(); - } - } } Window { @@ -75,8 +69,8 @@ StudioControls.ComboBox { height: Math.min(800, Math.min(col.height + 2, Screen.height - y - 40)) // 40: some bottom margin to cover OS bottom toolbar flags: Qt.Popup | Qt.FramelessWindowHint - onActiveChanged: { - if (!active && !root.hover) + onActiveFocusItemChanged: { + if (!window.activeFocusItem && !root.indicator.hover && root.popup.opened) root.popup.close() } @@ -89,6 +83,7 @@ StudioControls.ComboBox { HelperWidgets.ScrollView { anchors.fill: parent anchors.margins: 1 + clip: true Column { id: col diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/images/preview0.png b/share/qtcreator/qmldesigner/effectMakerQmlSources/images/preview0.png new file mode 100644 index 0000000000000000000000000000000000000000..3d2552f30a9b195a5abb4505f0204782ef86f87c GIT binary patch literal 22504 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 aKZeKC&lk#j9M=IIyx{5T=d#Wzp$P!fh#K7h literal 0 HcmV?d00001 From 93b1c18aeb01e801e6134f5f5809563aa9b75d24 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 20 Sep 2023 19:30:41 +0200 Subject: [PATCH 057/130] QmlDesigner: Remove ko statement if changing type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I28a0e796e3f3da0da584e1b8e58c8821f2777d61 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Henning Gründl --- .../components/connectioneditor/connectionmodel.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 54965013a44..50ad727fe22 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -737,6 +737,11 @@ void ConnectionModelBackendDelegate::changeActionType(ActionType actionType) ConnectionEditorStatements::MatchedStatement &okStatement = ConnectionEditorStatements::okStatement(m_handler); + ConnectionEditorStatements::MatchedStatement &koStatement + = ConnectionEditorStatements::koStatement(m_handler); + + koStatement = ConnectionEditorStatements::EmptyBlock(); + //We expect a valid id on the root node const QString validId = model->connectionView()->rootModelNode().validId(); QString statementSource = generateDefaultStatement(actionType, validId); From 105bc1b25332ca10334b7472de547800e9f5c0d0 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 21 Sep 2023 10:08:17 +0200 Subject: [PATCH 058/130] QmlDesigner: Remove search temporarily Change-Id: I4759c9bd4c831b2e4df9522fe9598fe2d8099bed Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Brook Cronin Reviewed-by: Thomas Hartmann --- .../qmldesigner/connectionseditor/Main.qml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/Main.qml b/share/qtcreator/qmldesigner/connectionseditor/Main.qml index c84f6dcdcbb..0b3752ac812 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Main.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Main.qml @@ -27,7 +27,7 @@ Rectangle { Rectangle { id: toolbar width: parent.width - height: StudioTheme.Values.doubleToolbarHeight + height: StudioTheme.Values.toolbarHeight color: StudioTheme.Values.themeToolbarBackground Column { @@ -38,13 +38,14 @@ Rectangle { anchors.rightMargin: StudioTheme.Values.toolbarHorizontalMargin spacing: StudioTheme.Values.toolbarColumnSpacing - StudioControls.SearchBox { - id: searchBox - width: parent.width - style: StudioTheme.Values.searchControlStyle + // Temporarily remove search until functionality is provided by backend + //StudioControls.SearchBox { + // id: searchBox + // width: parent.width + // style: StudioTheme.Values.searchControlStyle - onSearchChanged: function(searchText) {} - } + // onSearchChanged: function(searchText) {} + //} Row { id: row From 08272b400396a41725edddd18bad2e84c9bc5075 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 19 Sep 2023 17:14:51 +0300 Subject: [PATCH 059/130] QmlDesigner: Only play animations related to selected particle system When particle mode is enabled in 3D view, only the animations related to the selected particle system are played instead of all animations in the scene. Timeline animations are never played, as those are controlled with timeline controls. Animation is considered related to selected particle system if the animation target is either descendant or ancestor of the selected system, or the system itself. Fixes: QDS-10678 Change-Id: Iaaaec14f86d61c7aba2347b16bc757fc188601a0 Reviewed-by: Mahmoud Badri --- .../qt5informationnodeinstanceserver.cpp | 63 ++++++++++++++++++- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 663adcf3e17..3491aa85a02 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -533,9 +533,66 @@ void Qt5InformationNodeInstanceServer::handleParticleSystemSelected(QQuick3DPart } }); - const auto anim = animations(); - for (auto a : anim) - a->restart(); + if (m_targetParticleSystem) { + auto checkAncestor = [](QObject *checkObj, QObject *ancestor) -> bool { + QObject *parent = checkObj->parent(); + while (parent) { + if (parent == ancestor) + return true; + parent = parent->parent(); + } + return false; + }; + auto isAnimContainer = [](QObject *o) -> bool { + return ServerNodeInstance::isSubclassOf(o, "QQuickParallelAnimation") + || ServerNodeInstance::isSubclassOf(o, "QQuickSequentialAnimation"); + }; + + const QVector anims = animations(); + QSet containers; + for (auto a : anims) { + // Stop all animations by default. We only want to run animations related to currently + // active particle system and nothing else. + a->stop(); + + // Timeline animations are controlled by timeline controls, so exclude those + if (ServerNodeInstance::isSubclassOf(a, "QQuickTimelineAnimation")) + continue; + + if (ServerNodeInstance::isSubclassOf(a, "QQuickPropertyAnimation") + || ServerNodeInstance::isSubclassOf(a, "QQuickPropertyAction")) { + QObject *target = a->property("target").value(); + if (target != m_targetParticleSystem + && !checkAncestor(target, m_targetParticleSystem) + && !checkAncestor(m_targetParticleSystem, target)) { + continue; + } + } else { + continue; + } + + QObject *animParent = a->parent(); + bool isContained = isAnimContainer(animParent); + if (isContained) { + // We only want to start the toplevel container animations + while (isContained) { + if (isAnimContainer(animParent->parent())) { + animParent = animParent->parent(); + isContained = true; + } else { + containers.insert(qobject_cast(animParent)); + isContained = false; + } + } + } else { + a->restart(); + } + } + + // Activate necessary container animations + for (auto container : std::as_const(containers)) + container->restart(); + } } static QString baseProperty(const QString &property) From 56ee8ec9e0d05e646623d1da338b81e033d81c2c Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Wed, 20 Sep 2023 12:11:02 +0200 Subject: [PATCH 060/130] Add QtQuick.color to to the isColor comparison function Change-Id: I2b4495c4ad6c7e1b22358688a719b37524a1ebb4 Reviewed-by: Marco Bubke Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 7a825ccc8ba..ef6ed17acc4 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2816,7 +2816,7 @@ bool NodeMetaInfo::isColor() const auto type = m_privateData->qualfiedTypeName(); - return type == "QColor" || type == "color"; + return type == "QColor" || type == "color" || type == "QtQuick.color"; } } From bbd80f712e05143cfcff9273faa5f3bcdf94f9b4 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 20 Sep 2023 11:28:35 +0300 Subject: [PATCH 061/130] QmlDesigner: Add Json and CSV Icons to the IconFonts Change-Id: I2e4f3b92530f9301713bfe7796d1d1e60f291054 Reviewed-by: Reviewed-by: Heikki Paulaharju Reviewed-by: Thomas Hartmann Reviewed-by: Brook Cronin Reviewed-by: Qt CI Patch Build Bot --- .../imports/StudioTheme/InternalConstants.qml | 667 +++++++++--------- .../imports/StudioTheme/icons.ttf | Bin 59592 -> 64484 bytes .../components/componentcore/theme.h | 17 + 3 files changed, 359 insertions(+), 325 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index 23c37e165f2..168f3c37908 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -23,331 +23,348 @@ QtObject { readonly property string addTable: "\u0028" readonly property string add_medium: "\u0029" readonly property string add_small: "\u002A" - readonly property string adsClose: "\u002B" - readonly property string adsDetach: "\u002C" - readonly property string adsDropDown: "\u002D" - readonly property string alias: "\u002E" - readonly property string aliasAnimated: "\u002F" - readonly property string alignBottom: "\u0030" - readonly property string alignCenterHorizontal: "\u0031" - readonly property string alignCenterVertical: "\u0032" - readonly property string alignLeft: "\u0033" - readonly property string alignRight: "\u0034" - readonly property string alignTo: "\u0035" - readonly property string alignToCam_medium: "\u0036" - readonly property string alignToCamera_small: "\u0037" - readonly property string alignToObject_small: "\u0038" - readonly property string alignToView_medium: "\u0039" - readonly property string alignTop: "\u003A" - readonly property string anchorBaseline: "\u003B" - readonly property string anchorBottom: "\u003C" - readonly property string anchorFill: "\u003D" - readonly property string anchorLeft: "\u003E" - readonly property string anchorRight: "\u003F" - readonly property string anchorTop: "\u0040" - readonly property string anchors_small: "\u0041" - readonly property string animatedProperty: "\u0042" - readonly property string annotationBubble: "\u0043" - readonly property string annotationDecal: "\u0044" - readonly property string annotations_large: "\u0045" - readonly property string annotations_small: "\u0046" - readonly property string applyMaterialToSelected: "\u0047" - readonly property string apply_medium: "\u0048" - readonly property string apply_small: "\u0049" - readonly property string arrange_small: "\u004A" - readonly property string arrow_small: "\u004B" - readonly property string assign: "\u004C" - readonly property string attach_medium: "\u004D" - readonly property string back_medium: "\u004E" - readonly property string backspace_small: "\u004F" - readonly property string bevelAll: "\u0050" - readonly property string bevelCorner: "\u0051" - readonly property string bezier_medium: "\u0052" - readonly property string binding_medium: "\u0053" - readonly property string bounds_small: "\u0054" - readonly property string branch_medium: "\u0055" - readonly property string camera_small: "\u0056" - readonly property string centerHorizontal: "\u0057" - readonly property string centerVertical: "\u0058" - readonly property string cleanLogs_medium: "\u0059" - readonly property string closeCross: "\u005A" - readonly property string closeFile_large: "\u005B" - readonly property string closeLink: "\u005C" - readonly property string close_small: "\u005D" - readonly property string code: "\u005E" - readonly property string codeEditor_medium: "\u005F" - readonly property string colorPopupClose: "\u0060" - readonly property string colorSelection_medium: "\u0061" - readonly property string columnsAndRows: "\u0062" - readonly property string cone_medium: "\u0063" - readonly property string cone_small: "\u0064" - readonly property string connection_small: "\u0065" - readonly property string connections_medium: "\u0066" - readonly property string copyLink: "\u0067" - readonly property string copyStyle: "\u0068" - readonly property string copy_small: "\u0069" - readonly property string cornerA: "\u006A" - readonly property string cornerB: "\u006B" - readonly property string cornersAll: "\u006C" - readonly property string createComponent_large: "\u006D" - readonly property string createComponent_small: "\u006E" - readonly property string create_medium: "\u006F" - readonly property string create_small: "\u0070" - readonly property string cube_medium: "\u0071" - readonly property string cube_small: "\u0072" - readonly property string curveDesigner: "\u0073" - readonly property string curveDesigner_medium: "\u0074" - readonly property string curveEditor: "\u0075" - readonly property string customMaterialEditor: "\u0076" - readonly property string cylinder_medium: "\u0077" - readonly property string cylinder_small: "\u0078" - readonly property string decisionNode: "\u0079" - readonly property string deleteColumn: "\u007A" - readonly property string deleteMaterial: "\u007B" - readonly property string deleteRow: "\u007C" - readonly property string deleteTable: "\u007D" - readonly property string delete_medium: "\u007E" - readonly property string delete_small: "\u007F" - readonly property string designMode_large: "\u0080" - readonly property string detach: "\u0081" - readonly property string directionalLight_small: "\u0082" - readonly property string distributeBottom: "\u0083" - readonly property string distributeCenterHorizontal: "\u0084" - readonly property string distributeCenterVertical: "\u0085" - readonly property string distributeLeft: "\u0086" - readonly property string distributeOriginBottomRight: "\u0087" - readonly property string distributeOriginCenter: "\u0088" - readonly property string distributeOriginNone: "\u0089" - readonly property string distributeOriginTopLeft: "\u008A" - readonly property string distributeRight: "\u008B" - readonly property string distributeSpacingHorizontal: "\u008C" - readonly property string distributeSpacingVertical: "\u008D" - readonly property string distributeTop: "\u008E" - readonly property string download: "\u008F" - readonly property string downloadUnavailable: "\u0090" - readonly property string downloadUpdate: "\u0091" - readonly property string downloaded: "\u0092" - readonly property string dragmarks: "\u0093" - readonly property string duplicate_small: "\u0094" - readonly property string edit: "\u0095" - readonly property string editComponent_large: "\u0096" - readonly property string editComponent_small: "\u0097" - readonly property string editLightOff_medium: "\u0098" - readonly property string editLightOn_medium: "\u0099" - readonly property string edit_medium: "\u009A" - readonly property string edit_small: "\u009B" - readonly property string effects: "\u009D" - readonly property string events_small: "\u009E" - readonly property string export_medium: "\u009F" - readonly property string eyeDropper: "\u00A0" - readonly property string favorite: "\u00A1" - readonly property string fitAll_medium: "\u00A2" - readonly property string fitSelected_small: "\u00A3" - readonly property string fitSelection_medium: "\u00A4" - readonly property string fitToView_medium: "\u00A5" - readonly property string flowAction: "\u00A6" - readonly property string flowTransition: "\u00A7" - readonly property string fontStyleBold: "\u00A8" - readonly property string fontStyleItalic: "\u00A9" - readonly property string fontStyleStrikethrough: "\u00AA" - readonly property string fontStyleUnderline: "\u00AB" - readonly property string forward_medium: "\u00AC" - readonly property string globalOrient_medium: "\u00AE" - readonly property string gradient: "\u00AF" - readonly property string gridView: "\u00B0" - readonly property string grid_medium: "\u00B1" - readonly property string group_small: "\u00B2" - readonly property string help: "\u00B3" - readonly property string home_large: "\u00B4" - readonly property string idAliasOff: "\u00B5" - readonly property string idAliasOn: "\u00B6" - readonly property string import_medium: "\u00B7" - readonly property string imported: "\u00B8" - readonly property string importedModels_small: "\u00B9" - readonly property string infinity: "\u00BA" - readonly property string invisible_medium: "\u00BB" - readonly property string invisible_small: "\u00BC" - readonly property string keyframe: "\u00BD" - readonly property string languageList_medium: "\u00BE" - readonly property string layouts_small: "\u00BF" - readonly property string lights_small: "\u00C0" - readonly property string linear_medium: "\u00C1" - readonly property string linkTriangle: "\u00C2" - readonly property string linked: "\u00C3" - readonly property string listView: "\u00C4" - readonly property string list_medium: "\u00C5" - readonly property string localOrient_medium: "\u00C6" - readonly property string lockOff: "\u00C7" - readonly property string lockOn: "\u00C8" - readonly property string loopPlayback_medium: "\u00C9" - readonly property string materialBrowser_medium: "\u00CA" - readonly property string materialPreviewEnvironment: "\u00CB" - readonly property string materialPreviewModel: "\u00CC" - readonly property string material_medium: "\u00CD" - readonly property string maxBar_small: "\u00CE" - readonly property string mergeCells: "\u00CF" - readonly property string merge_small: "\u00D0" - readonly property string minus: "\u00D1" - readonly property string mirror: "\u00D2" - readonly property string more_medium: "\u00D3" - readonly property string mouseArea_small: "\u00D4" - readonly property string moveDown_medium: "\u00D5" - readonly property string moveInwards_medium: "\u00D6" - readonly property string moveUp_medium: "\u00D7" - readonly property string moveUpwards_medium: "\u00D8" - readonly property string move_medium: "\u00D9" - readonly property string newMaterial: "\u00DA" - readonly property string nextFile_large: "\u00DB" - readonly property string normalBar_small: "\u00DC" - readonly property string openLink: "\u00DD" - readonly property string openMaterialBrowser: "\u00DE" - readonly property string orientation: "\u00DF" - readonly property string orthCam_medium: "\u00E0" - readonly property string orthCam_small: "\u00E1" - readonly property string paddingEdge: "\u00E2" - readonly property string paddingFrame: "\u00E3" - readonly property string particleAnimation_medium: "\u00E4" - readonly property string pasteStyle: "\u00E5" - readonly property string paste_small: "\u00E6" - readonly property string pause: "\u00E7" - readonly property string perspectiveCam_medium: "\u00E8" - readonly property string perspectiveCam_small: "\u00E9" - readonly property string pin: "\u00EA" - readonly property string plane_medium: "\u00EB" - readonly property string plane_small: "\u00EC" - readonly property string play: "\u00ED" - readonly property string playFill_medium: "\u00EE" - readonly property string playOutline_medium: "\u00EF" - readonly property string plus: "\u00F0" - readonly property string pointLight_small: "\u00F1" - readonly property string positioners_small: "\u00F2" - readonly property string previewEnv_medium: "\u00F3" - readonly property string previousFile_large: "\u00F4" - readonly property string promote: "\u00F5" - readonly property string properties_medium: "\u00F6" - readonly property string readOnly: "\u00F7" - readonly property string recordFill_medium: "\u00F8" - readonly property string recordOutline_medium: "\u00F9" - readonly property string redo: "\u00FA" - readonly property string reload_medium: "\u00FB" - readonly property string remove_medium: "\u00FC" - readonly property string remove_small: "\u00FD" - readonly property string rename_small: "\u00FE" - readonly property string replace_small: "\u00FF" - readonly property string resetView_small: "\u0100" - readonly property string restartParticles_medium: "\u0101" - readonly property string reverseOrder_medium: "\u0102" - readonly property string roatate_medium: "\u0103" - readonly property string rotationFill: "\u0104" - readonly property string rotationOutline: "\u0105" - readonly property string runProjFill_large: "\u0106" - readonly property string runProjOutline_large: "\u0107" - readonly property string s_anchors: "\u0108" - readonly property string s_annotations: "\u0109" - readonly property string s_arrange: "\u010A" - readonly property string s_boundingBox: "\u010B" - readonly property string s_component: "\u010C" - readonly property string s_connections: "\u010D" - readonly property string s_edit: "\u010E" - readonly property string s_enterComponent: "\u010F" - readonly property string s_eventList: "\u0110" - readonly property string s_group: "\u0111" - readonly property string s_layouts: "\u0112" - readonly property string s_merging: "\u0113" - readonly property string s_mouseArea: "\u0114" - readonly property string s_positioners: "\u0115" - readonly property string s_selection: "\u0116" - readonly property string s_snapping: "\u0117" - readonly property string s_timeline: "\u0118" - readonly property string s_visibility: "\u0119" - readonly property string saveLogs_medium: "\u011A" - readonly property string scale_medium: "\u011B" - readonly property string search: "\u011C" - readonly property string search_small: "\u011D" - readonly property string sectionToggle: "\u011E" - readonly property string selectFill_medium: "\u011F" - readonly property string selectOutline_medium: "\u0120" - readonly property string selectParent_small: "\u0121" - readonly property string selection_small: "\u0122" - readonly property string settings_medium: "\u0123" - readonly property string signal_small: "\u0124" - readonly property string snapping_conf_medium: "\u0125" - readonly property string snapping_medium: "\u0126" - readonly property string snapping_small: "\u0127" - readonly property string sphere_medium: "\u0128" - readonly property string sphere_small: "\u0129" - readonly property string splitColumns: "\u012A" - readonly property string splitRows: "\u012B" - readonly property string spotLight_small: "\u012C" - readonly property string stackedContainer_small: "\u012D" - readonly property string startNode: "\u012E" - readonly property string step_medium: "\u012F" - readonly property string stop_medium: "\u0130" - readonly property string testIcon: "\u0131" - readonly property string textAlignBottom: "\u0132" - readonly property string textAlignCenter: "\u0133" - readonly property string textAlignJustified: "\u0134" - readonly property string textAlignLeft: "\u0135" - readonly property string textAlignMiddle: "\u0136" - readonly property string textAlignRight: "\u0137" - readonly property string textAlignTop: "\u0138" - readonly property string textBulletList: "\u0139" - readonly property string textFullJustification: "\u013A" - readonly property string textNumberedList: "\u013B" - readonly property string textures_medium: "\u013C" - readonly property string tickIcon: "\u013D" - readonly property string tickMark_small: "\u013E" - readonly property string timeline_small: "\u013F" - readonly property string toEndFrame_medium: "\u0140" - readonly property string toNextFrame_medium: "\u0141" - readonly property string toPrevFrame_medium: "\u0142" - readonly property string toStartFrame_medium: "\u0143" - readonly property string topToolbar_annotations: "\u0144" - readonly property string topToolbar_closeFile: "\u0145" - readonly property string topToolbar_designMode: "\u0146" - readonly property string topToolbar_enterComponent: "\u0147" - readonly property string topToolbar_home: "\u0148" - readonly property string topToolbar_makeComponent: "\u0149" - readonly property string topToolbar_navFile: "\u014A" - readonly property string topToolbar_runProject: "\u014B" - readonly property string translationCreateFiles: "\u014C" - readonly property string translationCreateReport: "\u014D" - readonly property string translationExport: "\u014E" - readonly property string translationImport: "\u014F" - readonly property string translationSelectLanguages: "\u0150" - readonly property string translationTest: "\u0151" - readonly property string transparent: "\u0152" - readonly property string triState: "\u0153" - readonly property string triangleArcA: "\u0154" - readonly property string triangleArcB: "\u0155" - readonly property string triangleCornerA: "\u0156" - readonly property string triangleCornerB: "\u0157" - readonly property string unLinked: "\u0158" - readonly property string undo: "\u0159" - readonly property string unify_medium: "\u015A" - readonly property string unpin: "\u015B" - readonly property string upDownIcon: "\u015C" - readonly property string upDownSquare2: "\u015D" - readonly property string updateAvailable_medium: "\u015E" - readonly property string updateContent_medium: "\u015F" - readonly property string visibilityOff: "\u0160" - readonly property string visibilityOn: "\u0161" - readonly property string visible_medium: "\u0162" - readonly property string visible_small: "\u0163" - readonly property string wildcard: "\u0164" - readonly property string wizardsAutomotive: "\u0165" - readonly property string wizardsDesktop: "\u0166" - readonly property string wizardsGeneric: "\u0167" - readonly property string wizardsMcuEmpty: "\u0168" - readonly property string wizardsMcuGraph: "\u0169" - readonly property string wizardsMobile: "\u016A" - readonly property string wizardsUnknown: "\u016B" - readonly property string zoomAll: "\u016C" - readonly property string zoomIn: "\u016D" - readonly property string zoomIn_medium: "\u016E" - readonly property string zoomOut: "\u016F" - readonly property string zoomOut_medium: "\u0170" - readonly property string zoomSelection: "\u0171" + readonly property string addcolumnleft_medium: "\u002B" + readonly property string addcolumnright_medium: "\u002C" + readonly property string addrowabove_medium: "\u002D" + readonly property string addrowbelow_medium: "\u002E" + readonly property string adsClose: "\u002F" + readonly property string adsDetach: "\u0030" + readonly property string adsDropDown: "\u0031" + readonly property string alias: "\u0032" + readonly property string aliasAnimated: "\u0033" + readonly property string alignBottom: "\u0034" + readonly property string alignCenterHorizontal: "\u0035" + readonly property string alignCenterVertical: "\u0036" + readonly property string alignLeft: "\u0037" + readonly property string alignRight: "\u0038" + readonly property string alignTo: "\u0039" + readonly property string alignToCam_medium: "\u003A" + readonly property string alignToCamera_small: "\u003B" + readonly property string alignToObject_small: "\u003C" + readonly property string alignToView_medium: "\u003D" + readonly property string alignTop: "\u003E" + readonly property string anchorBaseline: "\u003F" + readonly property string anchorBottom: "\u0040" + readonly property string anchorFill: "\u0041" + readonly property string anchorLeft: "\u0042" + readonly property string anchorRight: "\u0043" + readonly property string anchorTop: "\u0044" + readonly property string anchors_small: "\u0045" + readonly property string animatedProperty: "\u0046" + readonly property string annotationBubble: "\u0047" + readonly property string annotationDecal: "\u0048" + readonly property string annotations_large: "\u0049" + readonly property string annotations_small: "\u004A" + readonly property string applyMaterialToSelected: "\u004B" + readonly property string apply_medium: "\u004C" + readonly property string apply_small: "\u004D" + readonly property string arrange_small: "\u004E" + readonly property string arrow_small: "\u004F" + readonly property string assign: "\u0050" + readonly property string attach_medium: "\u0051" + readonly property string back_medium: "\u0052" + readonly property string backspace_small: "\u0053" + readonly property string bevelAll: "\u0054" + readonly property string bevelCorner: "\u0055" + readonly property string bezier_medium: "\u0056" + readonly property string binding_medium: "\u0057" + readonly property string bounds_small: "\u0058" + readonly property string branch_medium: "\u0059" + readonly property string camera_small: "\u005A" + readonly property string centerHorizontal: "\u005B" + readonly property string centerVertical: "\u005C" + readonly property string cleanLogs_medium: "\u005D" + readonly property string closeCross: "\u005E" + readonly property string closeFile_large: "\u005F" + readonly property string closeLink: "\u0060" + readonly property string close_small: "\u0061" + readonly property string code: "\u0062" + readonly property string codeEditor_medium: "\u0063" + readonly property string codeview_medium: "\u0064" + readonly property string colorPopupClose: "\u0065" + readonly property string colorSelection_medium: "\u0066" + readonly property string columnsAndRows: "\u0067" + readonly property string cone_medium: "\u0068" + readonly property string cone_small: "\u0069" + readonly property string connection_small: "\u006A" + readonly property string connections_medium: "\u006B" + readonly property string copyLink: "\u006C" + readonly property string copyStyle: "\u006D" + readonly property string copy_small: "\u006E" + readonly property string cornerA: "\u006F" + readonly property string cornerB: "\u0070" + readonly property string cornersAll: "\u0071" + readonly property string createComponent_large: "\u0072" + readonly property string createComponent_small: "\u0073" + readonly property string create_medium: "\u0074" + readonly property string create_small: "\u0075" + readonly property string cube_medium: "\u0076" + readonly property string cube_small: "\u0077" + readonly property string curveDesigner: "\u0078" + readonly property string curveDesigner_medium: "\u0079" + readonly property string curveEditor: "\u007A" + readonly property string customMaterialEditor: "\u007B" + readonly property string cylinder_medium: "\u007C" + readonly property string cylinder_small: "\u007D" + readonly property string decisionNode: "\u007E" + readonly property string deleteColumn: "\u007F" + readonly property string deleteMaterial: "\u0080" + readonly property string deleteRow: "\u0081" + readonly property string deleteTable: "\u0082" + readonly property string delete_medium: "\u0083" + readonly property string delete_small: "\u0084" + readonly property string deletecolumn_medium: "\u0085" + readonly property string deleterow_medium: "\u0086" + readonly property string designMode_large: "\u0087" + readonly property string detach: "\u0088" + readonly property string directionalLight_small: "\u0089" + readonly property string distributeBottom: "\u008A" + readonly property string distributeCenterHorizontal: "\u008B" + readonly property string distributeCenterVertical: "\u008C" + readonly property string distributeLeft: "\u008D" + readonly property string distributeOriginBottomRight: "\u008E" + readonly property string distributeOriginCenter: "\u008F" + readonly property string distributeOriginNone: "\u0090" + readonly property string distributeOriginTopLeft: "\u0091" + readonly property string distributeRight: "\u0092" + readonly property string distributeSpacingHorizontal: "\u0093" + readonly property string distributeSpacingVertical: "\u0094" + readonly property string distributeTop: "\u0095" + readonly property string download: "\u0096" + readonly property string downloadUnavailable: "\u0097" + readonly property string downloadUpdate: "\u0098" + readonly property string downloadcsv_large: "\u0099" + readonly property string downloadcsv_medium: "\u009A" + readonly property string downloaded: "\u009B" + readonly property string downloadjson_large: "\u009D" + readonly property string downloadjson_medium: "\u009E" + readonly property string dragmarks: "\u009F" + readonly property string duplicate_small: "\u00A0" + readonly property string edit: "\u00A1" + readonly property string editComponent_large: "\u00A2" + readonly property string editComponent_small: "\u00A3" + readonly property string editLightOff_medium: "\u00A4" + readonly property string editLightOn_medium: "\u00A5" + readonly property string edit_medium: "\u00A6" + readonly property string edit_small: "\u00A7" + readonly property string effects: "\u00A8" + readonly property string events_small: "\u00A9" + readonly property string export_medium: "\u00AA" + readonly property string eyeDropper: "\u00AB" + readonly property string favorite: "\u00AC" + readonly property string fitAll_medium: "\u00AE" + readonly property string fitSelected_small: "\u00AF" + readonly property string fitSelection_medium: "\u00B0" + readonly property string fitToView_medium: "\u00B1" + readonly property string flowAction: "\u00B2" + readonly property string flowTransition: "\u00B3" + readonly property string fontStyleBold: "\u00B4" + readonly property string fontStyleItalic: "\u00B5" + readonly property string fontStyleStrikethrough: "\u00B6" + readonly property string fontStyleUnderline: "\u00B7" + readonly property string forward_medium: "\u00B8" + readonly property string globalOrient_medium: "\u00B9" + readonly property string gradient: "\u00BA" + readonly property string gridView: "\u00BB" + readonly property string grid_medium: "\u00BC" + readonly property string group_small: "\u00BD" + readonly property string help: "\u00BE" + readonly property string home_large: "\u00BF" + readonly property string idAliasOff: "\u00C0" + readonly property string idAliasOn: "\u00C1" + readonly property string import_medium: "\u00C2" + readonly property string imported: "\u00C3" + readonly property string importedModels_small: "\u00C4" + readonly property string infinity: "\u00C5" + readonly property string invisible_medium: "\u00C6" + readonly property string invisible_small: "\u00C7" + readonly property string keyframe: "\u00C8" + readonly property string languageList_medium: "\u00C9" + readonly property string layouts_small: "\u00CA" + readonly property string lights_small: "\u00CB" + readonly property string linear_medium: "\u00CC" + readonly property string linkTriangle: "\u00CD" + readonly property string linked: "\u00CE" + readonly property string listView: "\u00CF" + readonly property string list_medium: "\u00D0" + readonly property string localOrient_medium: "\u00D1" + readonly property string lockOff: "\u00D2" + readonly property string lockOn: "\u00D3" + readonly property string loopPlayback_medium: "\u00D4" + readonly property string materialBrowser_medium: "\u00D5" + readonly property string materialPreviewEnvironment: "\u00D6" + readonly property string materialPreviewModel: "\u00D7" + readonly property string material_medium: "\u00D8" + readonly property string maxBar_small: "\u00D9" + readonly property string mergeCells: "\u00DA" + readonly property string merge_small: "\u00DB" + readonly property string minus: "\u00DC" + readonly property string mirror: "\u00DD" + readonly property string more_medium: "\u00DE" + readonly property string mouseArea_small: "\u00DF" + readonly property string moveDown_medium: "\u00E0" + readonly property string moveInwards_medium: "\u00E1" + readonly property string moveUp_medium: "\u00E2" + readonly property string moveUpwards_medium: "\u00E3" + readonly property string move_medium: "\u00E4" + readonly property string newMaterial: "\u00E5" + readonly property string nextFile_large: "\u00E6" + readonly property string normalBar_small: "\u00E7" + readonly property string openLink: "\u00E8" + readonly property string openMaterialBrowser: "\u00E9" + readonly property string orientation: "\u00EA" + readonly property string orthCam_medium: "\u00EB" + readonly property string orthCam_small: "\u00EC" + readonly property string paddingEdge: "\u00ED" + readonly property string paddingFrame: "\u00EE" + readonly property string particleAnimation_medium: "\u00EF" + readonly property string pasteStyle: "\u00F0" + readonly property string paste_small: "\u00F1" + readonly property string pause: "\u00F2" + readonly property string perspectiveCam_medium: "\u00F3" + readonly property string perspectiveCam_small: "\u00F4" + readonly property string pin: "\u00F5" + readonly property string plane_medium: "\u00F6" + readonly property string plane_small: "\u00F7" + readonly property string play: "\u00F8" + readonly property string playFill_medium: "\u00F9" + readonly property string playOutline_medium: "\u00FA" + readonly property string plus: "\u00FB" + readonly property string pointLight_small: "\u00FC" + readonly property string positioners_small: "\u00FD" + readonly property string previewEnv_medium: "\u00FE" + readonly property string previousFile_large: "\u00FF" + readonly property string promote: "\u0100" + readonly property string properties_medium: "\u0101" + readonly property string readOnly: "\u0102" + readonly property string recordFill_medium: "\u0103" + readonly property string recordOutline_medium: "\u0104" + readonly property string redo: "\u0105" + readonly property string reload_medium: "\u0106" + readonly property string remove_medium: "\u0107" + readonly property string remove_small: "\u0108" + readonly property string rename_small: "\u0109" + readonly property string replace_small: "\u010A" + readonly property string resetView_small: "\u010B" + readonly property string restartParticles_medium: "\u010C" + readonly property string reverseOrder_medium: "\u010D" + readonly property string roatate_medium: "\u010E" + readonly property string rotationFill: "\u010F" + readonly property string rotationOutline: "\u0110" + readonly property string runProjFill_large: "\u0111" + readonly property string runProjOutline_large: "\u0112" + readonly property string s_anchors: "\u0113" + readonly property string s_annotations: "\u0114" + readonly property string s_arrange: "\u0115" + readonly property string s_boundingBox: "\u0116" + readonly property string s_component: "\u0117" + readonly property string s_connections: "\u0118" + readonly property string s_edit: "\u0119" + readonly property string s_enterComponent: "\u011A" + readonly property string s_eventList: "\u011B" + readonly property string s_group: "\u011C" + readonly property string s_layouts: "\u011D" + readonly property string s_merging: "\u011E" + readonly property string s_mouseArea: "\u011F" + readonly property string s_positioners: "\u0120" + readonly property string s_selection: "\u0121" + readonly property string s_snapping: "\u0122" + readonly property string s_timeline: "\u0123" + readonly property string s_visibility: "\u0124" + readonly property string saveLogs_medium: "\u0125" + readonly property string scale_medium: "\u0126" + readonly property string search: "\u0127" + readonly property string search_small: "\u0128" + readonly property string sectionToggle: "\u0129" + readonly property string selectFill_medium: "\u012A" + readonly property string selectOutline_medium: "\u012B" + readonly property string selectParent_small: "\u012C" + readonly property string selection_small: "\u012D" + readonly property string settings_medium: "\u012E" + readonly property string signal_small: "\u012F" + readonly property string snapping_conf_medium: "\u0130" + readonly property string snapping_medium: "\u0131" + readonly property string snapping_small: "\u0132" + readonly property string sortascending_medium: "\u0133" + readonly property string sortdescending_medium: "\u0134" + readonly property string sphere_medium: "\u0135" + readonly property string sphere_small: "\u0136" + readonly property string splitColumns: "\u0137" + readonly property string splitRows: "\u0138" + readonly property string spotLight_small: "\u0139" + readonly property string stackedContainer_small: "\u013A" + readonly property string startNode: "\u013B" + readonly property string step_medium: "\u013C" + readonly property string stop_medium: "\u013D" + readonly property string testIcon: "\u013E" + readonly property string textAlignBottom: "\u013F" + readonly property string textAlignCenter: "\u0140" + readonly property string textAlignJustified: "\u0141" + readonly property string textAlignLeft: "\u0142" + readonly property string textAlignMiddle: "\u0143" + readonly property string textAlignRight: "\u0144" + readonly property string textAlignTop: "\u0145" + readonly property string textBulletList: "\u0146" + readonly property string textFullJustification: "\u0147" + readonly property string textNumberedList: "\u0148" + readonly property string textures_medium: "\u0149" + readonly property string tickIcon: "\u014A" + readonly property string tickMark_small: "\u014B" + readonly property string timeline_small: "\u014C" + readonly property string toEndFrame_medium: "\u014D" + readonly property string toNextFrame_medium: "\u014E" + readonly property string toPrevFrame_medium: "\u014F" + readonly property string toStartFrame_medium: "\u0150" + readonly property string topToolbar_annotations: "\u0151" + readonly property string topToolbar_closeFile: "\u0152" + readonly property string topToolbar_designMode: "\u0153" + readonly property string topToolbar_enterComponent: "\u0154" + readonly property string topToolbar_home: "\u0155" + readonly property string topToolbar_makeComponent: "\u0156" + readonly property string topToolbar_navFile: "\u0157" + readonly property string topToolbar_runProject: "\u0158" + readonly property string translationCreateFiles: "\u0159" + readonly property string translationCreateReport: "\u015A" + readonly property string translationExport: "\u015B" + readonly property string translationImport: "\u015C" + readonly property string translationSelectLanguages: "\u015D" + readonly property string translationTest: "\u015E" + readonly property string transparent: "\u015F" + readonly property string triState: "\u0160" + readonly property string triangleArcA: "\u0161" + readonly property string triangleArcB: "\u0162" + readonly property string triangleCornerA: "\u0163" + readonly property string triangleCornerB: "\u0164" + readonly property string unLinked: "\u0165" + readonly property string undo: "\u0166" + readonly property string unify_medium: "\u0167" + readonly property string unpin: "\u0168" + readonly property string upDownIcon: "\u0169" + readonly property string upDownSquare2: "\u016A" + readonly property string updateAvailable_medium: "\u016B" + readonly property string updateContent_medium: "\u016C" + readonly property string uploadcsv_large: "\u016D" + readonly property string uploadcsv_medium: "\u016E" + readonly property string uploadjson_large: "\u016F" + readonly property string uploadjson_medium: "\u0170" + readonly property string visibilityOff: "\u0171" + readonly property string visibilityOn: "\u0172" + readonly property string visible_medium: "\u0173" + readonly property string visible_small: "\u0174" + readonly property string wildcard: "\u0175" + readonly property string wizardsAutomotive: "\u0176" + readonly property string wizardsDesktop: "\u0177" + readonly property string wizardsGeneric: "\u0178" + readonly property string wizardsMcuEmpty: "\u0179" + readonly property string wizardsMcuGraph: "\u017A" + readonly property string wizardsMobile: "\u017B" + readonly property string wizardsUnknown: "\u017C" + readonly property string zoomAll: "\u017D" + readonly property string zoomIn: "\u017E" + readonly property string zoomIn_medium: "\u017F" + readonly property string zoomOut: "\u0180" + readonly property string zoomOut_medium: "\u0181" + readonly property string zoomSelection: "\u0182" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index 8e35e027799a14cc76cfd852c9bf2191461793f8..4d18734ddac41d554999461e30cf272872037ed2 100644 GIT binary patch delta 6049 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 O)5-(Y6NrK7@HhbP(_hs9 delta 1121 zcmaFzo%zH?=6VK31_lORh6V;^h5$FW5Z?uf)g}xKFV-+HFv$1^>l;PRNRwq?V2og3 zU`R;LO)Sv(ul<{WfhC23fhjDxtVDr9k*Sh_fo%^10|QHXVsXL${|w9w3@?^2FfeeW z=TxTE`TqF9z`&Woz;G}kBQ-Ha8;S@;zLP36UiR84Pbqow=ConKb3xYkz$iU!{HN82W-{va=H}eYy1_p+Qx+{_( zG}DUYNsPA4K@6KFJ21LV-p7uOZ8 zf7}k-IovheCwQEA?(sVDPUGFj`;5okZhF5>cZPS<1-(#R>&}67# z7-Trh@Q;y*k)Bb6QG?MsqZ`I-#wNx~j31bAn7EnDG37AzFkNFNU?yW`V0Ogpi+PxN zmiZftJd14>Z|W`8EKMx^EDJ2>St(ehSRJr>Wi4YJW&OzdkByE^ip@NmEjDLt)oj~r zx7aD!71?dDdtlFHuVbHNf5m~rq0UjlG0Sn4<0q#Gr#(*ZoXwmkIq!1*=Mv_!!sU&t zmTR5sCpR^>IJX?P32x`y#oUWL{5-yRF7f=~<>$4+>y@{JcfFhUDIY5z51$!6XMA4y zI{5bZUh`A(tMEJGFXdn6e<&a>;6%WiK(WB6z@ET;fuDj@g3^NK1os603W*9?6LKw- zCp0K@Tj-ZCqp)>hH^SY*3WB^LC BeDweT diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index 34e07700021..6edd3338f5c 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -31,6 +31,10 @@ public: addTable, add_medium, add_small, + addcolumnleft_medium, + addcolumnright_medium, + addrowabove_medium, + addrowbelow_medium, adsClose, adsDetach, adsDropDown, @@ -84,6 +88,7 @@ public: close_small, code, codeEditor_medium, + codeview_medium, colorPopupClose, colorSelection_medium, columnsAndRows, @@ -116,6 +121,8 @@ public: deleteTable, delete_medium, delete_small, + deletecolumn_medium, + deleterow_medium, designMode_large, detach, directionalLight_small, @@ -134,7 +141,11 @@ public: download, downloadUnavailable, downloadUpdate, + downloadcsv_large, + downloadcsv_medium, downloaded, + downloadjson_large, + downloadjson_medium, dragmarks, duplicate_small, edit, @@ -282,6 +293,8 @@ public: snapping_conf_medium, snapping_medium, snapping_small, + sortascending_medium, + sortdescending_medium, sphere_medium, sphere_small, splitColumns, @@ -338,6 +351,10 @@ public: upDownSquare2, updateAvailable_medium, updateContent_medium, + uploadcsv_large, + uploadcsv_medium, + uploadjson_large, + uploadjson_medium, visibilityOff, visibilityOn, visible_medium, From 79a41ec7a945686c4e237329759b25b41d908fc4 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Thu, 21 Sep 2023 11:17:19 +0200 Subject: [PATCH 062/130] QmlDesigner: Fix text editor widget stretch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-10730 Change-Id: I4e46e53032378b0af7b63921eddb5b0b84670dc4 Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- .../components/bindingeditor/abstracteditordialog.cpp | 3 ++- .../components/bindingeditor/actioneditordialog.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp index 8ee7ef1b9ed..35b9c501745 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp @@ -123,7 +123,8 @@ void AbstractEditorDialog::setupUIComponents() m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); m_verticalLayout->addLayout(m_comboBoxLayout); - m_verticalLayout->addWidget(m_editorWidget); + //editor widget has to stretch the most among the other siblings: + m_verticalLayout->addWidget(m_editorWidget, 10); m_verticalLayout->addWidget(m_buttonBox); this->resize(660, 240); diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.cpp index b5bb15c30cf..86824ac2656 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.cpp @@ -399,6 +399,16 @@ void ActionEditorDialog::showControls(bool show) m_assignmentSourceItem->setVisible(show); if (m_assignmentSourceProperty) m_assignmentSourceProperty->setVisible(show); + + if (m_stackedLayout) + m_stackedLayout->setEnabled(show); + if (m_actionLayout) + m_actionLayout->setEnabled(show); + if (m_assignmentLayout) + m_assignmentLayout->setEnabled(show); + + if (m_comboBoxLayout) + m_comboBoxLayout->setEnabled(show); } void ActionEditorDialog::setMultiline(bool multiline) From 25b540d995b74ac786e4dfe7e700f7c6033b262a Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 21 Sep 2023 13:41:23 +0300 Subject: [PATCH 063/130] QmlDesigner: Fix ScrollView not working on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caused by (5676e612982d3c87f403677bcb63e0b6343f9b7d) Change-Id: I9dd45a31326e061e5ef6e3cddfcd069ce9132aee Reviewed-by: Henning Gründl --- .../imports/HelperWidgets/ScrollView.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml index 34342958f79..f17d2025aa2 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import QtQuick.Controls import StudioControls as StudioControls import StudioTheme 1.0 as StudioTheme From e4b8af404be0a5c1ac92709435c6ce2ed0e134e9 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 21 Sep 2023 13:53:39 +0300 Subject: [PATCH 064/130] QmlDesigner: Fix effect maker popup doesn't close on focus out on mac Fixes: QDS-10515 Change-Id: Icaef0532bd2427e9cac326f40143e158ca8c5e97 Reviewed-by: Miikka Heikkinen --- .../qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml | 2 +- .../qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml index 6d1b1cec960..b076e152cc0 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml @@ -47,7 +47,7 @@ StudioControls.ComboBox { width: row.width + 2 // 2: scrollView left and right 1px margins height: Math.min(800, Math.min(row.height + 2, Screen.height - y - 40)) // 40: some bottom margin to cover OS bottom toolbar - flags: Qt.Popup | Qt.FramelessWindowHint + flags: Qt.Dialog | Qt.FramelessWindowHint onActiveFocusItemChanged: { if (!window.activeFocusItem && !root.indicator.hover && root.popup.opened) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml index 445935b532e..a53a923ca6b 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml @@ -67,7 +67,7 @@ StudioControls.ComboBox { width: col.width + 2 // 2: scrollView left and right 1px margins height: Math.min(800, Math.min(col.height + 2, Screen.height - y - 40)) // 40: some bottom margin to cover OS bottom toolbar - flags: Qt.Popup | Qt.FramelessWindowHint + flags: Qt.Dialog | Qt.FramelessWindowHint onActiveFocusItemChanged: { if (!window.activeFocusItem && !root.indicator.hover && root.popup.opened) From 0fa45358a49c36515071eb79a9fc65a6dbb244b5 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 21 Sep 2023 13:51:46 +0300 Subject: [PATCH 065/130] QmlDesigner: Always add QtQuick import when creating a new component Fixes: QDS-10733 Change-Id: I916a7d9da82a2c28c4cef81f6777fb65728b9c2a Reviewed-by: Marco Bubke Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/designercore/model/rewriterview.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index ea344d3e62b..68b6426b8fa 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -1029,6 +1029,11 @@ void RewriterView::moveToComponent(const ModelNode &modelNode) const QList nodes = modelNode.allSubModelNodesAndThisNode(); QSet directPaths; + // Always add QtQuick import + QString quickImport = model()->qtQuickItemMetaInfo().requiredImportString(); + if (!quickImport.isEmpty()) + directPaths.insert(quickImport); + for (const ModelNode &partialNode : nodes) { QString importStr = partialNode.metaInfo().requiredImportString(); if (importStr.size()) From f47e7d9d6c62e2769dafd0748eb6f7a25789ffd5 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 21 Sep 2023 13:55:26 +0200 Subject: [PATCH 066/130] QmlDesigner: Fix transient scroll bar disappearing Task-number: QDS-10689 Task-number: QDS-10690 Change-Id: Ib74e27f06ca24ff13f3000297c88306a7a9bb579 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../contentLibraryQmlSource/ContentLibrary.qml | 9 +++++++++ .../itemLibraryQmlSources/AddModuleView.qml | 2 ++ .../qmldesigner/itemLibraryQmlSources/ItemsView.qml | 10 ++++++++++ 3 files changed, 21 insertions(+) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml index 1932af0eef9..306f5405b30 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml @@ -13,6 +13,11 @@ import ContentLibraryBackend Item { id: root + property bool adsFocus: false + // objectName is used by the dock widget to find this particular ScrollView + // and set the ads focus on it. + objectName: "__mainSrollView" + // Called also from C++ to close context menu on focus out function closeContextMenu() { @@ -96,6 +101,7 @@ Item { ContentLibraryMaterialsView { id: materialsView + adsFocus: root.adsFocus width: root.width searchBox: searchBox @@ -110,6 +116,7 @@ Item { ContentLibraryTexturesView { id: texturesView + adsFocus: root.adsFocus width: root.width model: ContentLibraryBackend.texturesModel sectionCategory: "ContentLib_Tex" @@ -120,6 +127,7 @@ Item { ContentLibraryTexturesView { id: environmentsView + adsFocus: root.adsFocus width: root.width model: ContentLibraryBackend.environmentsModel sectionCategory: "ContentLib_Env" @@ -130,6 +138,7 @@ Item { ContentLibraryEffectsView { id: effectsView + adsFocus: root.adsFocus width: root.width searchBox: searchBox diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AddModuleView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AddModuleView.qml index 585c6a6d14c..6ed7052b8e5 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AddModuleView.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AddModuleView.qml @@ -11,6 +11,8 @@ import ItemLibraryBackend Column { id: root + property alias adsFocus: listView.adsFocus + spacing: 5 signal back() diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml index 05d8d6db9c2..a90fbaaa15f 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml @@ -46,6 +46,12 @@ itemLibraryModel [ */ Item { id: itemsView + + property bool adsFocus: false + // objectName is used by the dock widget to find this particular ScrollView + // and set the ads focus on it. + objectName: "__mainSrollView" + property string importToRemove property string importToAdd property string componentSource @@ -217,6 +223,7 @@ Item { id: verticalView HelperWidgets.ScrollView { id: verticalScrollView + adsFocus: itemsView.adsFocus anchors.fill: parent clip: true interactive: !itemContextMenu.opened && !moduleContextMenu.opened && !ItemLibraryBackend.rootView.isDragging @@ -327,6 +334,7 @@ Item { leftPadding: 5 HelperWidgets.ScrollView { id: horizontalScrollView + adsFocus: itemsView.adsFocus width: 270 height: parent.height clip: true @@ -427,6 +435,7 @@ Item { } HelperWidgets.ScrollView { id: itemScrollView + adsFocus: itemsView.adsFocus width: itemsView.width - 275 height: itemsView.height interactive: !itemContextMenu.opened && !moduleContextMenu.opened && !ItemLibraryBackend.rootView.isDragging @@ -468,6 +477,7 @@ Item { Component { id: addModuleView AddModuleView { + adsFocus: itemsView.adsFocus onBack: isAddModuleView = false } } From 502aed8cfb40ce42bbe312e47bd10e3393b723e3 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Thu, 21 Sep 2023 15:24:02 +0300 Subject: [PATCH 067/130] QmlDesigner: Add ShaderToolsPrivate lib to effect maker plugin Task-number: QDS-10499 Change-Id: Id9807cfc50198f29a31d22b046d89ec9485367e0 Reviewed-by: Eike Ziller Reviewed-by: Mahmoud Badri --- src/plugins/effectmakernew/CMakeLists.txt | 3 +-- src/plugins/effectmakernew/effectmakermodel.h | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/effectmakernew/CMakeLists.txt b/src/plugins/effectmakernew/CMakeLists.txt index 09d70a7fea0..b8842687e5e 100644 --- a/src/plugins/effectmakernew/CMakeLists.txt +++ b/src/plugins/effectmakernew/CMakeLists.txt @@ -6,7 +6,7 @@ add_qtc_plugin(EffectMakerNew QtCreator::Core QtCreator::QmlDesigner DEPENDS Qt::Core - QtCreator::Utils Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick Qt::ShaderTools + QtCreator::Utils Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick Qt::ShaderTools Qt::ShaderToolsPrivate SOURCES effectmakerplugin.cpp effectmakerplugin.h effectmakerwidget.cpp effectmakerwidget.h @@ -22,6 +22,5 @@ add_qtc_plugin(EffectMakerNew effectmakercontextobject.cpp effectmakercontextobject.h shaderfeatures.cpp shaderfeatures.h syntaxhighlighterdata.cpp syntaxhighlighterdata.h - BUILD_DEFAULT OFF ) diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index 08441bfb434..0dd858d5026 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -9,6 +9,8 @@ #include #include +#include + namespace EffectMaker { class CompositionNode; @@ -125,6 +127,7 @@ private: QString m_vertexShader; QStringList m_defaultRootVertexShader; QStringList m_defaultRootFragmentShader; + QShaderBaker m_baker; // Used in exported QML, at root of the file QString m_exportedRootPropertiesString; // Used in exported QML, at ShaderEffect component of the file From 15051d112e9b8ecbfbb9e65526a8699959d2da61 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 21 Sep 2023 16:13:43 +0300 Subject: [PATCH 068/130] QmlDesigner: Preserve size of small asset view images Small images get blurry if scaled up, so just show the original image in the thumbnail if it is smaller than the thumbnail. Fixes: QDS-10671 Change-Id: I76b5b1c5b316fb66e7df71602383b894dd7bc635 Reviewed-by: Mahmoud Badri --- .../assetsLibraryQmlSources/AssetDelegate.qml | 2 +- .../assetslibrary/assetslibraryiconprovider.cpp | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml index 6a511c72524..edaf038ac8d 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml @@ -327,7 +327,7 @@ TreeViewDelegate { sourceSize.width: 48 sourceSize.height: 48 asynchronous: true - fillMode: Image.PreserveAspectFit + fillMode: Image.Pad source: thumbnailImage.__computeSource() function __computeSource() { diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp index 838af4d63d1..dc5a1c97411 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp @@ -56,8 +56,15 @@ Thumbnail AssetsLibraryIconProvider::createThumbnail(const QString &id, const QS originalSize = KtxImage(id).dimensions(); } - if (requestedSize.isValid()) - pixmap = pixmap.scaled(requestedSize, Qt::KeepAspectRatio); + if (requestedSize.isValid()) { + double ratio = requestedSize.width() / 48.; + if (ratio * pixmap.size().width() > requestedSize.width() + || ratio * pixmap.size().height() > requestedSize.height()) { + pixmap = pixmap.scaled(requestedSize, Qt::KeepAspectRatio); + } else if (!qFuzzyCompare(ratio, 1.)) { + pixmap = pixmap.scaled(pixmap.size() * ratio, Qt::KeepAspectRatio); + } + } return Thumbnail{pixmap, originalSize, assetType, fileSize}; } From 4cc7148f0d39e66156dd606161e9b0c16f0963e1 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 21 Sep 2023 17:07:06 +0200 Subject: [PATCH 069/130] QmlDesigner: Disable layout context menu for MCU Change-Id: If46a89ec138f60a58eb9f4b1336c6c4aaac796eb Reviewed-by: Aleksei German --- .../componentcore/designeractionmanager.cpp | 6 ++--- .../componentcore/modelnodeoperations.cpp | 27 +++++-------------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 628d49ca074..2d7d452ecd5 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -1240,9 +1240,9 @@ bool isPositioner(const SelectionContext &context) bool layoutOptionVisible(const SelectionContext &context) { - return selectionCanBeLayoutedAndQtQuickLayoutPossible(context) - || singleSelectionAndInQtQuickLayout(context) - || isLayout(context); + return (selectionCanBeLayoutedAndQtQuickLayoutPossible(context) + || singleSelectionAndInQtQuickLayout(context) || isLayout(context)) + && !DesignerMcuManager::instance().isMCUProject(); } bool positionOptionVisible(const SelectionContext &context) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index fa533d14de4..bedfdb7dd14 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -519,13 +519,8 @@ void layoutFlowPositioner(const SelectionContext &selectionContext) void layoutRowLayout(const SelectionContext &selectionContext) { try { - if (DesignerMcuManager::instance().isMCUProject()) { - layoutHelperFunction(selectionContext, "QtQuick.Row", compareByX); - } - else { - LayoutInGridLayout::ensureLayoutImport(selectionContext); - layoutHelperFunction(selectionContext, "QtQuick.Layouts.RowLayout", compareByX); - } + LayoutInGridLayout::ensureLayoutImport(selectionContext); + layoutHelperFunction(selectionContext, "QtQuick.Layouts.RowLayout", compareByX); } catch (RewritingException &e) { //better safe than sorry e.showException(); } @@ -534,13 +529,8 @@ void layoutRowLayout(const SelectionContext &selectionContext) void layoutColumnLayout(const SelectionContext &selectionContext) { try { - if (DesignerMcuManager::instance().isMCUProject()) { - layoutHelperFunction(selectionContext, "QtQuick.Column", compareByX); - } - else { - LayoutInGridLayout::ensureLayoutImport(selectionContext); - layoutHelperFunction(selectionContext, "QtQuick.Layouts.ColumnLayout", compareByY); - } + LayoutInGridLayout::ensureLayoutImport(selectionContext); + layoutHelperFunction(selectionContext, "QtQuick.Layouts.ColumnLayout", compareByY); } catch (RewritingException &e) { //better safe than sorry e.showException(); } @@ -551,13 +541,8 @@ void layoutGridLayout(const SelectionContext &selectionContext) try { Q_ASSERT(!DesignerMcuManager::instance().isMCUProject()); //remove this line when grids are finally supported - if (DesignerMcuManager::instance().isMCUProject()) { - //qt for mcu doesn't support any grids yet - } - else { - LayoutInGridLayout::ensureLayoutImport(selectionContext); - LayoutInGridLayout::layout(selectionContext); - } + LayoutInGridLayout::ensureLayoutImport(selectionContext); + LayoutInGridLayout::layout(selectionContext); } catch (RewritingException &e) { //better safe than sorry e.showException(); } From 3d1f5ba8d2d25709d8a4d1b858953392819ec90c Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 21 Sep 2023 17:07:53 +0200 Subject: [PATCH 070/130] QmlDesigner: Remove layout import version Task-number: QDS-10154 Change-Id: Ibd4dd77830010b9945731c21c34d3f0a63084bf0 Reviewed-by: Thomas Hartmann --- .../components/componentcore/layoutingridlayout.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp index 89a5868d89d..9c6ef49a05a 100644 --- a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp +++ b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp @@ -196,7 +196,7 @@ void LayoutInGridLayout::doIt() static bool hasQtQuickLayoutImport(const SelectionContext &context) { if (context.view() && context.view()->model()) { - Import import = Import::createLibraryImport(QStringLiteral("QtQuick.Layouts"), QStringLiteral("1.0")); + Import import = Import::createLibraryImport(QStringLiteral("QtQuick.Layouts"), {}); return context.view()->model()->hasImport(import, true, true); } @@ -206,7 +206,7 @@ static bool hasQtQuickLayoutImport(const SelectionContext &context) void LayoutInGridLayout::ensureLayoutImport(const SelectionContext &context) { if (!hasQtQuickLayoutImport(context)) { - Import layoutImport = Import::createLibraryImport("QtQuick.Layouts", "1.0"); + Import layoutImport = Import::createLibraryImport("QtQuick.Layouts", {}); context.view()-> model()->changeImports({layoutImport}, {}); } } From a4b1e1ccdc978384792524a9871a321d99fc9165 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 21 Sep 2023 17:35:03 +0200 Subject: [PATCH 071/130] QmlDesigner: Deactivate layout shortcut for MCU Change-Id: I80c247155aabae54b826e950e7c9a93ab75dfc47 Reviewed-by: Aleksei German Reviewed-by: Qt CI Patch Build Bot --- .../componentcore/designeractionmanager.cpp | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 2d7d452ecd5..098376f0fe9 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -1810,29 +1810,31 @@ void DesignerActionManager::createDefaultDesignerActions() &isStackedContainerAndIndexCanBeIncreased, &isStackedContainer)); - addDesignerAction(new ModelNodeAction( - layoutRowLayoutCommandId, - layoutRowLayoutDisplayName, - Utils::Icon({{":/qmldesigner/icon/designeractions/images/row.png", - Utils::Theme::IconsBaseColor}}).icon(), - layoutRowLayoutToolTip, - layoutCategory, - QKeySequence("Ctrl+u"), - 2, - &layoutRowLayout, - &selectionCanBeLayoutedAndQtQuickLayoutPossible)); + addDesignerAction( + new ModelNodeAction(layoutRowLayoutCommandId, + layoutRowLayoutDisplayName, + Utils::Icon({{":/qmldesigner/icon/designeractions/images/row.png", + Utils::Theme::IconsBaseColor}}) + .icon(), + layoutRowLayoutToolTip, + layoutCategory, + QKeySequence("Ctrl+u"), + 2, + &layoutRowLayout, + &selectionCanBeLayoutedAndQtQuickLayoutPossibleAndNotMCU)); - addDesignerAction(new ModelNodeAction( - layoutColumnLayoutCommandId, - layoutColumnLayoutDisplayName, - Utils::Icon({{":/qmldesigner/icon/designeractions/images/column.png", - Utils::Theme::IconsBaseColor}}).icon(), - layoutColumnLayoutToolTip, - layoutCategory, - QKeySequence("Ctrl+l"), - 3, - &layoutColumnLayout, - &selectionCanBeLayoutedAndQtQuickLayoutPossible)); + addDesignerAction( + new ModelNodeAction(layoutColumnLayoutCommandId, + layoutColumnLayoutDisplayName, + Utils::Icon({{":/qmldesigner/icon/designeractions/images/column.png", + Utils::Theme::IconsBaseColor}}) + .icon(), + layoutColumnLayoutToolTip, + layoutCategory, + QKeySequence("Ctrl+l"), + 3, + &layoutColumnLayout, + &selectionCanBeLayoutedAndQtQuickLayoutPossibleAndNotMCU)); addDesignerAction(new ModelNodeAction( layoutGridLayoutCommandId, From 47ce3beecf6a527d3b68e9952360abdd1f77c2a8 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Thu, 21 Sep 2023 15:51:43 +0200 Subject: [PATCH 072/130] QmlDesigner: Fix transient scrollbars in editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-10731 Change-Id: I23f4fcabc4f39143b18918b76395489b07aed510 Reviewed-by: Henning Gründl Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Ali Kianian --- .../components/bindingeditor/abstracteditordialog.cpp | 9 +++------ .../components/bindingeditor/bindingeditorwidget.cpp | 3 +++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp index 35b9c501745..9718691eb3f 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp @@ -3,18 +3,17 @@ #include "abstracteditordialog.h" +#include #include - #include #include #include -#include #include -#include -#include #include #include +#include +#include namespace QmlDesigner { @@ -102,8 +101,6 @@ void AbstractEditorDialog::setupJSEditor() m_editorWidget->setLineNumbersVisible(false); m_editorWidget->setMarksVisible(false); m_editorWidget->setCodeFoldingSupported(false); - m_editorWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - m_editorWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_editorWidget->setTabChangesFocus(true); } diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp index da52d37a7e0..109bcd0854e 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp @@ -19,6 +19,7 @@ #include #include +#include #include @@ -34,6 +35,8 @@ BindingEditorWidget::BindingEditorWidget() m_context->setContext(context); Core::ICore::addContextObject(m_context); + Utils::TransientScrollAreaSupport::support(this); + /* * We have to register our own active auto completion shortcut, because the original short cut will * use the cursor position of the original editor in the editor manager. From d1d23b09e890b4e7e451ee09f9a90de5fc323d4b Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Thu, 21 Sep 2023 14:48:21 +0200 Subject: [PATCH 073/130] QmlDesigner: Update Tooltips for Connections, Bindings and Properties This patch update tooltip texts for the fields within the Connections, Bindings and Properties sections of the Connections view. Fixes: QDS-10626 Change-Id: Iaf05aa8cdced051a3654ef0590cdbb36347625b6 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mats Honkamaa Reviewed-by: Thomas Hartmann --- .../connectionseditor/BindingsDialogForm.qml | 4 ++-- .../connectionseditor/BindingsListView.qml | 2 +- .../connectionseditor/ConnectionsDialog.qml | 2 +- .../ConnectionsDialogForm.qml | 24 +++++++++++++++++-- .../connectionseditor/ConnectionsListView.qml | 2 +- .../PropertiesDialogForm.qml | 6 ++--- .../connectionseditor/PropertiesListView.qml | 2 +- .../connectionseditor/StatementEditor.qml | 19 ++++++++------- 8 files changed, 41 insertions(+), 20 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml index c444512967f..749add63c43 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml @@ -25,13 +25,13 @@ Column { PopupLabel { width: root.columnWidth text: qsTr("From") - tooltip: qsTr("The Property to assign from.") + tooltip: qsTr("Sets the component and its property from which the value is copied.") } PopupLabel { width: root.columnWidth text: qsTr("To") - tooltip: qsTr("The Property to assign to.") + tooltip: qsTr("Sets the property of the selected component to which the copied value is assigned.") } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml index acebb0e0bf9..83db20646e0 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml @@ -199,7 +199,7 @@ ListView { HelperWidgets.ToolTipArea { id: toolTipArea - tooltip: qsTr("This is a test.") + tooltip: qsTr("Removes the binding.") anchors.fill: parent onClicked: { if (itemDelegate.ListView.isCurrentItem) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml index 53eaad0adf4..c7f0034b211 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml @@ -21,7 +21,7 @@ PopupDialog { anchors.verticalCenter: parent.verticalCenter HelperWidgets.ToolTipArea { anchors.fill: parent - tooltip: qsTr("Choose the target for the signal.") + tooltip: qsTr("Sets the Component that is connected to a Signal.") } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 57e34e81c9c..038ab46f86f 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -30,13 +30,13 @@ Column { PopupLabel { width: root.columnWidth text: qsTr("Signal") - tooltip: qsTr("The name of the signal.") + tooltip: qsTr("Sets an interaction method that connects to the Target component.") } PopupLabel { width: root.columnWidth text: qsTr("Action") - tooltip: qsTr("The type of the action.") + tooltip: qsTr("Sets an action that is associated with the selected Target component's Signal.") } } @@ -115,6 +115,7 @@ Column { style: StudioTheme.Values.connectionPopupButtonStyle width: 160 buttonIcon: qsTr("Add Condition") + tooltip: qsTr("Sets a logical condition for the selected Signal. It works with the properties of the Target component.") iconSize: StudioTheme.Values.baseFontSize iconFont: StudioTheme.Constants.font anchors.horizontalCenter: parent.horizontalCenter @@ -127,6 +128,7 @@ Column { style: StudioTheme.Values.connectionPopupButtonStyle width: 160 buttonIcon: qsTr("Remove Condition") + tooltip: qsTr("Removes the logical condition for the Target component.") iconSize: StudioTheme.Values.baseFontSize iconFont: StudioTheme.Constants.font anchors.horizontalCenter: parent.horizontalCenter @@ -177,6 +179,7 @@ Column { style: StudioTheme.Values.connectionPopupButtonStyle width: 160 buttonIcon: qsTr("Add Else Statement") + tooltip: qsTr("Sets an alternate condition for the previously defined logical condition.") iconSize: StudioTheme.Values.baseFontSize iconFont: StudioTheme.Constants.font anchors.horizontalCenter: parent.horizontalCenter @@ -190,6 +193,7 @@ Column { style: StudioTheme.Values.connectionPopupButtonStyle width: 160 buttonIcon: qsTr("Remove Else Statement") + tooltip: qsTr("Removes the alternate logical condition for the previously defined logical condition.") iconSize: StudioTheme.Values.baseFontSize iconFont: StudioTheme.Constants.font anchors.horizontalCenter: parent.horizontalCenter @@ -237,6 +241,22 @@ Column { horizontalAlignment: code.lineCount === 1 ? Text.AlignHCenter : Text.AlignLeft verticalAlignment: Text.AlignVCenter elide: Text.ElideRight + + } + + HelperWidgets.AbstractButton { + id: editorButton + + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: 4 + + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.edit_medium + tooltip: qsTr("Write the conditions for the components and the signals manually.") + onClicked: { + expressionDialogLoader.show() + } } Loader { diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml index 1827b4f8f43..b783627c827 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml @@ -195,7 +195,7 @@ ListView { HelperWidgets.ToolTipArea { id: toolTipArea - tooltip: qsTr("This is a test.") + tooltip: qsTr("Removes the connection.") anchors.fill: parent onClicked: { if (itemDelegate.ListView.isCurrentItem) diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml index 1c1f8db76bf..8d62daacf8d 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml @@ -21,7 +21,7 @@ Column { PopupLabel { text: qsTr("Type") - tooltip: qsTr("The type of the property") + tooltip: qsTr("Sets the category of the Local Custom Property.") } StudioControls.TopLevelComboBox { id: type @@ -41,13 +41,13 @@ Column { PopupLabel { width: root.columnWidth text: qsTr("Name") - tooltip: qsTr("The name of the property.") + tooltip: qsTr("Sets a name for the Local Custom Property.") } PopupLabel { width: root.columnWidth text: qsTr("Value") - tooltip: qsTr("The value of the property.") + tooltip: qsTr("Sets a valid Local Custom Property value.") } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index dc8baf9bdd7..53d1edea95a 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -203,7 +203,7 @@ ListView { HelperWidgets.ToolTipArea { id: toolTipArea - tooltip: qsTr("This is a test.") + tooltip: qsTr("Removes the property.") anchors.fill: parent onClicked: { if (itemDelegate.ListView.isCurrentItem) diff --git a/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml index c6f8a412b5b..f198d84a103 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml @@ -28,13 +28,13 @@ Column { PopupLabel { width: root.columnWidth text: qsTr("Item") - tooltip: qsTr("The target item of the function.") + tooltip: qsTr("Sets the component that is affected by the action of the Target component's Signal.") } PopupLabel { width: root.columnWidth text: qsTr("Method") - tooltip: qsTr("The name of the function.") + tooltip: qsTr("Sets the item component's method that is affected by the Target component's Signal.") } } @@ -74,12 +74,12 @@ Column { PopupLabel { width: root.columnWidth text: qsTr("From") - tooltip: qsTr("The Property to assign from.") + tooltip: qsTr("Sets the component and its property from which the value is copied when the Target component initiates the Signal.") } PopupLabel { width: root.columnWidth text: qsTr("To") - tooltip: qsTr("The Property to assign to.") + tooltip: qsTr("Sets the component and its property to which the copied value is assigned when the Target component initiates the Signal.") } } @@ -151,13 +151,13 @@ Column { PopupLabel { width: root.columnWidth text: qsTr("State Group") - tooltip: qsTr("The State Group.") + tooltip: qsTr("Sets a State Group that is accessed when the Target component initiates the Signal.") } PopupLabel { width: root.columnWidth text: qsTr("State") - tooltip: qsTr("The State .") + tooltip: qsTr("Sets a State within the assigned State Group that is accessed when the Target component initiates the Signal.") } } @@ -197,13 +197,13 @@ Column { PopupLabel { width: root.columnWidth text: qsTr("Item") - tooltip: qsTr("The Item.") + tooltip: qsTr("Sets the component that is affected by the action of the Target component's Signal.") } PopupLabel { width: root.columnWidth text: qsTr("Property") - tooltip: qsTr("The property of the item.") + tooltip: qsTr("Sets the property of the component that is affected by the action of the Target component's Signal.") } } @@ -240,6 +240,7 @@ Column { width: root.columnWidth visible: root.actionType === ConnectionModelStatementDelegate.SetProperty text: qsTr("Value") + tooltip: qsTr("Sets the value of the property of the component that is affected by the action of the Target component's Signal.") } StudioControls.TextField { @@ -260,7 +261,7 @@ Column { width: root.columnWidth visible: root.actionType === ConnectionModelStatementDelegate.PrintMessage text: qsTr("Message") - tooltip: qsTr("The message that is printed.") + tooltip: qsTr("Sets a text that is printed when the Signal of the Target component initiates.") } StudioControls.TextField { From 3229c355b26875797b92a40216616c9c0a23653d Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 22 Sep 2023 10:02:28 +0200 Subject: [PATCH 074/130] QmlDesigner: Deactivate auto complete temporarily Change-Id: I5f76e25133f99174c6ea2e5d40f8589c384e47f3 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../HelperWidgets/ExpressionTextField.qml | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExpressionTextField.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExpressionTextField.qml index 0888fdd1c7f..b968768043a 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExpressionTextField.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExpressionTextField.qml @@ -201,21 +201,22 @@ StudioControls.TextField { listView.model = list } - Keys.onSpacePressed: function(event) { - if (event.modifiers & Qt.ControlModifier) { - var list = autoComplete(textField.text, textField.cursorPosition, true, textField.completeOnlyTypes) - textField.prefix = textField.text.substring(0, textField.cursorPosition) - if (list.length && list[list.length - 1] === textField.prefix) - list.pop() + // Currently deactivated as it is causing a crash when calling autoComplete() + //Keys.onSpacePressed: function(event) { + // if (event.modifiers & Qt.ControlModifier) { + // var list = autoComplete(textField.text, textField.cursorPosition, true, textField.completeOnlyTypes) + // textField.prefix = textField.text.substring(0, textField.cursorPosition) + // if (list.length && list[list.length - 1] === textField.prefix) + // list.pop() - listView.model = list - textField.dotCompletion = false + // listView.model = list + // textField.dotCompletion = false - event.accepted = true; - } else { - event.accepted = false - } - } + // event.accepted = true + // } else { + // event.accepted = false + // } + //} Keys.onReturnPressed: function(event) { event.accepted = false From 1968b5e407bf43114e9cc63bd2690093730f0e1e Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 22 Sep 2023 11:13:40 +0200 Subject: [PATCH 075/130] QmlDesinger: Add transient ScrollBar FilterComboBox * Add transient ScrollBar to FilterComboBox * Apply new selected item style * Remove selected item icon Change-Id: Ic479cb23b3c0cff70447e5e7a89ab4e1b61f3489 Reviewed-by: Thomas Hartmann --- .../imports/HelperWidgets/UrlChooser.qml | 53 ++++++++-------- .../imports/StudioControls/FilterComboBox.qml | 62 ++++++++++--------- 2 files changed, 58 insertions(+), 57 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml index 8b06c20e5c4..485640bcf84 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml @@ -162,8 +162,6 @@ Row { id: delegateRoot width: comboBox.popup.width - comboBox.popup.leftPadding - comboBox.popup.rightPadding - - (comboBox.popupScrollBar.visible ? comboBox.popupScrollBar.contentItem.implicitWidth + 2 - : 0) // TODO Magic number height: StudioTheme.Values.height - 2 * StudioTheme.Values.border padding: 0 hoverEnabled: true @@ -176,32 +174,18 @@ Row { onClicked: comboBox.selectItem(delegateRoot.DelegateModel.itemsIndex) - indicator: Item { - id: itemDelegateIconArea - width: delegateRoot.height - height: delegateRoot.height - - Label { - id: itemDelegateIcon - text: StudioTheme.Constants.tickIcon - color: delegateRoot.highlighted ? StudioTheme.Values.themeTextSelectedTextColor - : StudioTheme.Values.themeTextColor - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.spinControlIconSizeMulti - visible: comboBox.currentIndex === delegateRoot.DelegateModel.itemsIndex ? true - : false - anchors.fill: parent - renderType: Text.NativeRendering - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - } - contentItem: Text { - leftPadding: itemDelegateIconArea.width + leftPadding: 8 text: name - color: delegateRoot.highlighted ? StudioTheme.Values.themeTextSelectedTextColor - : StudioTheme.Values.themeTextColor + color: { + if (!delegateRoot.enabled) + return comboBox.style.text.disabled + + if (comboBox.currentIndex === delegateRoot.DelegateModel.itemsIndex) + return comboBox.style.text.selectedText + + return comboBox.style.text.idle + } font: comboBox.font elide: Text.ElideRight verticalAlignment: Text.AlignVCenter @@ -212,8 +196,21 @@ Row { y: 0 width: delegateRoot.width height: delegateRoot.height - color: delegateRoot.highlighted ? StudioTheme.Values.themeInteraction - : "transparent" + color: { + if (!delegateRoot.enabled) + return "transparent" + + if (delegateRoot.hovered && comboBox.currentIndex === delegateRoot.DelegateModel.itemsIndex) + return comboBox.style.interactionHover + + if (comboBox.currentIndex === delegateRoot.DelegateModel.itemsIndex) + return comboBox.style.interaction + + if (delegateRoot.hovered) + return comboBox.style.background.hover + + return "transparent" + } } ToolTip { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml index 5593f46db00..8531d791348 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml @@ -277,32 +277,12 @@ Item { onClicked: control.selectItem(delegateRoot.DelegateModel.itemsIndex) - indicator: Item { - id: itemDelegateIconArea - width: delegateRoot.height - height: delegateRoot.height - - T.Label { - id: itemDelegateIcon - text: StudioTheme.Constants.tickIcon - color: delegateRoot.highlighted ? control.style.text.selectedText - : control.style.text.idle - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: control.style.smallIconFontSize - visible: control.currentIndex === delegateRoot.DelegateModel.itemsIndex ? true - : false - anchors.fill: parent - renderType: Text.NativeRendering - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - } - contentItem: Text { - leftPadding: itemDelegateIconArea.width + leftPadding: 8 text: name - color: delegateRoot.highlighted ? control.style.text.selectedText - : control.style.text.idle + color: control.currentIndex === delegateRoot.DelegateModel.itemsIndex + ? control.style.text.selectedText + : control.style.text.idle font: textInput.font elide: Text.ElideRight verticalAlignment: Text.AlignVCenter @@ -313,7 +293,22 @@ Item { y: 0 width: delegateRoot.width height: delegateRoot.height - color: delegateRoot.highlighted ? control.style.interaction : "transparent" + color: { + if (!itemDelegate.enabled) + return "transparent" + + if (itemDelegate.hovered + && control.currentIndex === delegateRoot.DelegateModel.itemsIndex) + return control.style.interactionHover + + if (control.currentIndex === delegateRoot.DelegateModel.itemsIndexx) + return control.style.interaction + + if (itemDelegate.hovered) + return control.style.background.hover + + return "transparent" + } } } @@ -669,9 +664,9 @@ Item { T.Popup { id: popup - x: textInput.x + control.style.borderWidth + x: textInput.x y: textInput.height - width: textInput.width - (control.style.borderWidth * 2) + width: textInput.width height: Math.min(popup.contentItem.implicitHeight + popup.topPadding + popup.bottomPadding, control.Window.height - popup.topMargin - popup.bottomMargin, control.style.maxComboBoxPopupHeight) @@ -694,9 +689,18 @@ Item { return null } - ScrollBar.vertical: ScrollBar { + HoverHandler { id: hoverHandler } + + ScrollBar.vertical: TransientScrollBar { id: popupScrollBar - visible: listView.height < listView.contentHeight + parent: listView + x: listView.width - verticalScrollBar.width + y: 0 + height: listView.availableHeight + orientation: Qt.Vertical + + show: (hoverHandler.hovered || popupScrollBar.inUse) + && popupScrollBar.isNeeded } } From 27d25261f947d27193446e87f72aff2f637f581e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 22 Sep 2023 11:18:25 +0200 Subject: [PATCH 076/130] QmlDesigner: Fix merge for tooltips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ic72817e01595ba47e4d510d136280b69057b7f5f Reviewed-by: Henning Gründl --- .../connectionseditor/ConnectionsDialogForm.qml | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 038ab46f86f..7f410f1c3ff 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -219,7 +219,7 @@ Column { HelperWidgets.AbstractButton { id: editorButton buttonIcon: StudioTheme.Constants.codeEditor_medium - tooltip: qsTr("Add something.") + tooltip: qsTr("Write the conditions for the components and the signals manually.") onClicked: expressionDialogLoader.show() } @@ -244,21 +244,6 @@ Column { } - HelperWidgets.AbstractButton { - id: editorButton - - anchors.top: parent.top - anchors.right: parent.right - anchors.margins: 4 - - style: StudioTheme.Values.viewBarButtonStyle - buttonIcon: StudioTheme.Constants.edit_medium - tooltip: qsTr("Write the conditions for the components and the signals manually.") - onClicked: { - expressionDialogLoader.show() - } - } - Loader { id: expressionDialogLoader parent: editor From 9f08e3f61164c805cfc35395cae724cea1225559 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 19 Sep 2023 14:22:19 +0200 Subject: [PATCH 077/130] QmlDesigner: Fix potential crash The view is not guranteed to be attached. In my case I closed the project and for now the dialog is not closed automatically in this case. Change-Id: If006fae2ddd78d6eefe604f6b0c44eb9e1fb725a Reviewed-by: Thomas Hartmann --- .../qmldesigner/components/connectioneditor/bindingmodel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index cc73f4b20a3..8ba1347bbfb 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -330,6 +330,7 @@ void BindingModelBackendDelegate::sourceNodeChanged() ConnectionView *view = model->connectionView(); QTC_ASSERT(view, return); + QTC_ASSERT(view->isAttached(), return ); const QString sourceNode = m_sourceNode.currentText(); const QString sourceProperty = m_sourceNodeProperty.currentText(); From 205af451773d6fe789d6429728f635f338a7362d Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 22 Sep 2023 12:02:43 +0200 Subject: [PATCH 078/130] QmlProject: Fix issues with cmakefiles.txt generation If file names contain spaces we simply add quotes around the filename. If file names contain special characters (!, % & etc), then we give a warning and skip the file to not break the build. Task-number: QDS-10745 Change-Id: Iae9497725816769a8922a0ef38f0dc05fb1bb582 Reviewed-by: Thomas Hartmann --- .../cmakegen/cmakegeneratordialog.cpp | 18 +++++--- .../cmakegen/cmakegeneratordialog.h | 5 ++- .../cmakegen/generatecmakelists.cpp | 42 ++++++++++++++++--- .../cmakegen/generatecmakelists.h | 4 ++ 4 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.cpp index 6ac7d9098dd..aa733b68af4 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.cpp @@ -2,9 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cmakegeneratordialog.h" -#include "cmakegeneratordialogtreemodel.h" -#include "generatecmakelistsconstants.h" #include "../qmlprojectmanagertr.h" +#include "cmakegeneratordialogtreemodel.h" #include #include @@ -21,10 +20,10 @@ using namespace Utils; namespace QmlProjectManager { namespace GenerateCmake { -CmakeGeneratorDialog::CmakeGeneratorDialog(const FilePath &rootDir, const FilePaths &files) - : QDialog(), - m_rootDir(rootDir), - m_files(files) +CmakeGeneratorDialog::CmakeGeneratorDialog(const FilePath &rootDir, + const FilePaths &files, + const FilePaths invalidFiles) + : QDialog(), m_rootDir(rootDir), m_files(files), m_invalidFiles(invalidFiles) { setWindowTitle(Tr::tr("Select Files to Generate")); @@ -106,6 +105,8 @@ FilePaths CmakeGeneratorDialog::getFilePaths() const QString FILE_CREATE_NOTIFICATION = Tr::tr("File %1 will be created.\n"); const QString FILE_OVERWRITE_NOTIFICATION = Tr::tr("File %1 will be overwritten.\n"); +const QString FILE_INVALID_NOTIFICATION = Tr::tr( + "File %1 contains invalid characters and will be skipped.\n"); void CmakeGeneratorDialog::refreshNotificationText() { @@ -119,6 +120,11 @@ void CmakeGeneratorDialog::refreshNotificationText() QList nodes = m_model->items(); + for (const auto &file : m_invalidFiles) { + cursor.insertImage(iformat); + cursor.insertText(QString(FILE_INVALID_NOTIFICATION).arg(file.displayName())); + } + for (CheckableFileTreeItem *node : nodes) { if (!m_files.contains(node->toFilePath())) continue; diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.h index 4c302b67368..fb15ec4de7c 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.h +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.h @@ -22,7 +22,9 @@ class CmakeGeneratorDialog : public QDialog Q_OBJECT public: - CmakeGeneratorDialog(const Utils::FilePath &rootDir, const Utils::FilePaths &files); + CmakeGeneratorDialog(const Utils::FilePath &rootDir, + const Utils::FilePaths &files, + const Utils::FilePaths invalidFiles); Utils::FilePaths getFilePaths(); public slots: @@ -40,6 +42,7 @@ private: QVariant m_warningIcon; Utils::FilePath m_rootDir; Utils::FilePaths m_files; + Utils::FilePaths m_invalidFiles; }; } //GenerateCmake diff --git a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp index 9603562dc68..cee9eb247e6 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp @@ -142,7 +142,7 @@ void onGenerateCmakeLists() for (const GeneratableFile &file: cmakeGen.fileQueue().queuedFiles()) allFiles.append(file.filePath); - CmakeGeneratorDialog dialog(rootDir, allFiles); + CmakeGeneratorDialog dialog(rootDir, allFiles, cmakeGen.invalidFileNames()); if (dialog.exec()) { FilePaths confirmedFiles = dialog.getFilePaths(); cmakeGen.filterFileQueue(confirmedFiles); @@ -334,6 +334,11 @@ bool CmakeFileGenerator::execute() return m_fileQueue.writeQueuedFiles(); } +FilePaths CmakeFileGenerator::invalidFileNames() const +{ + return m_invalidFileNames; +} + const char DO_NOT_EDIT_FILE_COMMENT[] = "### This file is automatically generated by Qt Design Studio.\n### Do not change\n\n"; const char ADD_SUBDIR[] = "add_subdirectory(%1)\n"; @@ -491,8 +496,7 @@ QStringList CmakeFileGenerator::getDirectoryResources(const FilePath &dir) FilePaths allFiles = dir.dirEntries(FILES_ONLY); for (FilePath &file : allFiles) { - if (!file.fileName().endsWith(".qml", Qt::CaseInsensitive) && - includeFile(file)) { + if (!file.fileName().endsWith(".qml", Qt::CaseInsensitive) && includeFile(file)) { moduleFiles.append(file.fileName()); } } @@ -519,10 +523,20 @@ QStringList CmakeFileGenerator::getDirectoryTreeQmls(const FilePath &dir) return qmlFileList; } +static void appendWidthQuotes(QStringList &list, const QString &string) +{ + if (string.contains(' ')) + list.append("\"" + string + "\""); + else + list.append(string); +} + QStringList CmakeFileGenerator::getDirectoryTreeResources(const FilePath &dir) { QStringList resourceFileList; + //for (const auto &string : getDirectoryResources(dir)) + // appendWidthQuotes(resourceFileList, string); resourceFileList.append(getDirectoryResources(dir)); FilePaths subDirsList = dir.dirEntries(DIRS_ONLY); @@ -531,9 +545,8 @@ QStringList CmakeFileGenerator::getDirectoryTreeResources(const FilePath &dir) continue; QStringList subDirResources = getDirectoryTreeResources(subDir); for (QString &resource : subDirResources) { - resourceFileList.append(subDir.fileName().append('/').append(resource)); + appendWidthQuotes(resourceFileList, subDir.fileName().append('/').append(resource)); } - } return resourceFileList; @@ -556,6 +569,18 @@ bool CmakeFileGenerator::isDirBlacklisted(const FilePath &dir) return (!dir.fileName().compare(DIRNAME_DESIGNER)); } +bool CmakeFileGenerator::validFileName(const Utils::FilePath &filePath) +{ + QStringList invalidChars = {"!", "\"", "£", "$", "%", "!", "^", "&", "*", "(", ")", "=", "+", + "'", ",", ";", ":", "#", "~", "{", "{", "[", "]", "<", ">", "?"}; + const QString baseName = filePath.baseName(); + for (const auto &c : invalidChars) { + if (baseName.contains(c)) + return false; + } + return true; +} + bool CmakeFileGenerator::includeFile(const FilePath &filePath) { if (m_checkFileIsInProject) { @@ -564,7 +589,12 @@ bool CmakeFileGenerator::includeFile(const FilePath &filePath) return false; } - return !isFileBlacklisted(filePath.fileName()); + if (validFileName(filePath)) + return !isFileBlacklisted(filePath.fileName()); + else + m_invalidFileNames.append(filePath); + + return false; } bool CmakeFileGenerator::generateEntryPointFiles(const FilePath &dir) diff --git a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.h b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.h index 6afa9cda05a..db025daa275 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.h +++ b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.h @@ -45,6 +45,7 @@ public: const FileQueue fileQueue() const; void filterFileQueue(const Utils::FilePaths &keepFiles); bool execute(); + Utils::FilePaths invalidFileNames() const; private: void generateMainCmake(const Utils::FilePath &rootDir); @@ -63,12 +64,15 @@ private: bool isFileBlacklisted(const QString &fileName); bool isDirBlacklisted(const Utils::FilePath &dir); bool includeFile(const Utils::FilePath &filePath); + bool validFileName(const Utils::FilePath &filePath); private: FileQueue m_fileQueue; QStringList m_resourceFileLocations; QStringList m_moduleNames; bool m_checkFileIsInProject; + + Utils::FilePaths m_invalidFileNames; }; } //GenerateCmake From e7e2fa7c0cd66ad33704a57fc535ffc51854e281 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 22 Sep 2023 10:52:14 +0300 Subject: [PATCH 079/130] QmlDesigner: Clear effect maker nodes on load Otherwise closing and repoening a project will add a duplicate of the effect nodes categories. Change-Id: Ibfe65ab66cca0b86cf34b1744c17b2c90280bba4 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- src/plugins/effectmakernew/effectmakernodesmodel.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/effectmakernew/effectmakernodesmodel.cpp b/src/plugins/effectmakernew/effectmakernodesmodel.cpp index 3b894d91575..3d3bb9f95d6 100644 --- a/src/plugins/effectmakernew/effectmakernodesmodel.cpp +++ b/src/plugins/effectmakernew/effectmakernodesmodel.cpp @@ -73,6 +73,8 @@ void EffectMakerNodesModel::loadModel() return; } + m_categories = {}; + QDirIterator itCategories(m_nodesPath.toString(), QDir::Dirs | QDir::NoDotAndDotDot); while (itCategories.hasNext()) { itCategories.next(); From 963f92ef4b81d409b607b1070281d2aa1d182fc8 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 19 Sep 2023 12:50:04 +0200 Subject: [PATCH 080/130] fix transientscroll.cpp compile against older Qt (tested 6.2.9) (cherry picked from commit 9ab6c7186d4486f6b8594c01d1aba38efd0bab50) Change-Id: I417aa1e0ef7c00181e85d8250e04b12ff6a24512 Reviewed-by: Tim Jenssen --- src/libs/utils/transientscroll.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/utils/transientscroll.cpp b/src/libs/utils/transientscroll.cpp index 3a0a9153769..65d082ffca8 100644 --- a/src/libs/utils/transientscroll.cpp +++ b/src/libs/utils/transientscroll.cpp @@ -517,7 +517,7 @@ void GlobalTransientSupport::support(QWidget *widget) if (area) TransientScrollAreaSupport::support(area); - for (QWidget *childWidget : widget->findChildren(Qt::FindChildOption::FindDirectChildrenOnly)) + for (QWidget *childWidget : widget->findChildren(QString(), Qt::FindChildOption::FindDirectChildrenOnly)) support(childWidget); } From 533f743edceef6256d48c55d9c48950576e23af3 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 20 Sep 2023 10:36:43 +0200 Subject: [PATCH 081/130] QmlDesigner: Fix warning about missing move constructor Change-Id: I79b2e7cc79ea3b6172133dfd07222451951249c6 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Aleksei German --- src/plugins/qmldesigner/designercore/include/annotation.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/annotation.h b/src/plugins/qmldesigner/designercore/include/annotation.h index 2026dddf736..647c1dafda1 100644 --- a/src/plugins/qmldesigner/designercore/include/annotation.h +++ b/src/plugins/qmldesigner/designercore/include/annotation.h @@ -26,8 +26,6 @@ public: GlobalAnnotationStatus(); GlobalAnnotationStatus(Status status); - ~GlobalAnnotationStatus() = default; - void setStatus(int statusId); void setStatus(Status status); Status status() const; From 02abf29fa5770f201a6d735b318f79c33edb3320 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 25 Sep 2023 10:44:31 +0300 Subject: [PATCH 082/130] QmlDesigner: Complete shaders baking for effect maker Also add versioning support Task-number: QDS-10499 Change-Id: If75222e3569d361b0d7bece70867e4020132c1bd Reviewed-by: Miikka Heikkinen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../effectmakernew/effectmakermodel.cpp | 82 ++++++++++++++++++- src/plugins/effectmakernew/effectmakermodel.h | 5 ++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 438fb8e4201..8b1836a5afa 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -14,9 +14,41 @@ namespace EffectMaker { +enum class FileType +{ + Binary, + Text +}; + +static bool writeToFile(const QByteArray &buf, const QString &filename, FileType fileType) +{ + QDir().mkpath(QFileInfo(filename).path()); + QFile f(filename); + QIODevice::OpenMode flags = QIODevice::WriteOnly | QIODevice::Truncate; + if (fileType == FileType::Text) + flags |= QIODevice::Text; + if (!f.open(flags)) { + qWarning() << "Failed to open file for writing:" << filename; + return false; + } + f.write(buf); + return true; +} + EffectMakerModel::EffectMakerModel(QObject *parent) : QAbstractListModel{parent} { + m_vertexShaderFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.vert.qsb"); + m_fragmentShaderFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.frag.qsb"); + // TODO: Will be revisted later when saving output files + if (m_vertexShaderFile.open()) + qInfo() << "Using temporary vs file:" << m_vertexShaderFile.fileName(); + if (m_fragmentShaderFile.open()) + qInfo() << "Using temporary fs file:" << m_fragmentShaderFile.fileName(); + + // Prepare baker + m_baker.setGeneratedShaderVariants({ QShader::StandardShader }); + updateBakedShaderVersions(); } QHash EffectMakerModel::roleNames() const @@ -97,6 +129,21 @@ void EffectMakerModel::removeNode(int idx) setIsEmpty(true); } +void EffectMakerModel::updateBakedShaderVersions() +{ + QList targets; + targets.append({ QShader::SpirvShader, QShaderVersion(100) }); // Vulkan 1.0 + targets.append({ QShader::HlslShader, QShaderVersion(50) }); // Shader Model 5.0 + targets.append({ QShader::MslShader, QShaderVersion(12) }); // Metal 1.2 + targets.append({ QShader::GlslShader, QShaderVersion(300, QShaderVersion::GlslEs) }); // GLES 3.0+ + targets.append({ QShader::GlslShader, QShaderVersion(410) }); // OpenGL 4.1+ + targets.append({ QShader::GlslShader, QShaderVersion(330) }); // OpenGL 3.3 + targets.append({ QShader::GlslShader, QShaderVersion(140) }); // OpenGL 3.1 + //TODO: Do we need support for legacy shaders 100, 120? + + m_baker.setGeneratedShaders(targets); +} + QString EffectMakerModel::fragmentShader() const { return m_fragmentShader; @@ -744,7 +791,40 @@ void EffectMakerModel::bakeShaders() updateCustomUniforms(); - // TODO: Shaders baking + setVertexShader(generateVertexShader()); + QString vs = m_vertexShader; + m_baker.setSourceString(vs.toUtf8(), QShader::VertexStage); + + QShader vertShader = m_baker.bake(); + if (!vertShader.isValid()) { + qWarning() << "Shader baking failed:" << qPrintable(m_baker.errorMessage()); + setEffectError(m_baker.errorMessage().split('\n').first(), ErrorVert); + } else { + QString filename = m_vertexShaderFile.fileName(); + writeToFile(vertShader.serialized(), filename, FileType::Binary); + resetEffectError(ErrorVert); + } + + setFragmentShader(generateFragmentShader()); + QString fs = m_fragmentShader; + m_baker.setSourceString(fs.toUtf8(), QShader::FragmentStage); + + QShader fragShader = m_baker.bake(); + if (!fragShader.isValid()) { + qWarning() << "Shader baking failed:" << qPrintable(m_baker.errorMessage()); + setEffectError(m_baker.errorMessage().split('\n').first(), ErrorFrag); + } else { + QString filename = m_fragmentShaderFile.fileName(); + writeToFile(fragShader.serialized(), filename, FileType::Binary); + resetEffectError(ErrorFrag); + } + + if (vertShader.isValid() && fragShader.isValid()) { + Q_EMIT shadersBaked(); + setShadersUpToDate(true); + } + + // TODO: Mark shaders as baked, required by export later } bool EffectMakerModel::shadersUpToDate() const diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index 0dd858d5026..aa418df303e 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -65,6 +66,7 @@ signals: void selectedIndexChanged(int idx); void effectErrorChanged(); void shadersUpToDateChanged(); + void shadersBaked(); private: enum Roles { @@ -90,6 +92,7 @@ private: const QString getVSUniforms(); const QString getFSUniforms(); + void updateBakedShaderVersions(); QString detectErrorMessage(const QString &errorMessage); EffectError effectError() const; void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1); @@ -128,6 +131,8 @@ private: QStringList m_defaultRootVertexShader; QStringList m_defaultRootFragmentShader; QShaderBaker m_baker; + QTemporaryFile m_fragmentShaderFile; + QTemporaryFile m_vertexShaderFile; // Used in exported QML, at root of the file QString m_exportedRootPropertiesString; // Used in exported QML, at ShaderEffect component of the file From d493563adbcef182c826697897fc4ec6fd33c30b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 25 Sep 2023 15:04:10 +0300 Subject: [PATCH 083/130] QmlDesigner: Change 3D grid stepping immediately on interval change Changes in position snap interval cause visible changes, as helper grid stepping is tied to this interval, so we notify puppet about every change to snap interval rather than just notifying the value when popup closes. Fixes: QDS-10750 Change-Id: If84f79530634c81f1d17f49d21929a442ff544f1 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/components/edit3d/edit3dview.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index a33c9c0019e..bb1b3e74985 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -966,8 +966,15 @@ void Edit3DView::createEdit3DActions() snapToggleTrigger); SelectionContextOperation snapConfigTrigger = [this](const SelectionContext &) { - if (!m_snapConfiguration) + if (!m_snapConfiguration) { m_snapConfiguration = new SnapConfiguration(this); + connect(m_snapConfiguration.data(), &SnapConfiguration::posIntChanged, + this, [this]() { + // Notify every change of position interval as that causes visible changes in grid + rootModelNode().setAuxiliaryData(edit3dSnapPosIntProperty, + m_snapConfiguration->posInt()); + }); + } m_snapConfiguration->showConfigDialog(resolveToolbarPopupPos(m_snapConfigAction.get())); }; From 794cb89c2acda78275b1add9057e2ac7ffed1b7c Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Mon, 25 Sep 2023 11:53:11 +0200 Subject: [PATCH 084/130] QmlProject: Fix skipping some nodes with QDS prefix This patch also contains the test data and updated test code for the converters to test QDS prefixed version of .qmlproject file. Task-number: QDS-10503 Change-Id: I496ba6f3d4d3cf90e75c2959c4196f4607677ea1 Reviewed-by: Marco Bubke Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Aleksei German --- .../buildsystem/projectitem/converters.cpp | 2 +- .../qmlprojectmanager/converters-test.cpp | 12 +- .../qmlprojectmanager/data/README.md | 6 + .../converter/test-set-3/testfile.jsontoqml | 98 ++++++++++ .../converter/test-set-3/testfile.qmlproject | 112 +++++++++++ .../converter/test-set-3/testfile.qmltojson | 178 ++++++++++++++++++ 6 files changed, 402 insertions(+), 6 deletions(-) create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index 8487c00cd7e..d380bedc757 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -272,7 +272,7 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) if (childNode->name().contains("files", Qt::CaseInsensitive)) { PropsPair propsPair; FileProps fileProps; - const QString childNodeName = childNode->name().toLower(); + const QString childNodeName = childNode->name().toLower().remove("qds."); const QmlJS::SimpleReaderNode::Property childNodeFilter = childNode->property("filter"); const QmlJS::SimpleReaderNode::Property childNodeDirectory = childNode->property( "directory"); diff --git a/tests/unit/tests/unittests/qmlprojectmanager/converters-test.cpp b/tests/unit/tests/unittests/qmlprojectmanager/converters-test.cpp index 4131ec48a56..a1ad263cf7c 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/converters-test.cpp +++ b/tests/unit/tests/unittests/qmlprojectmanager/converters-test.cpp @@ -10,7 +10,7 @@ namespace { constexpr QLatin1String localTestDataDir{UNITTEST_DIR "/qmlprojectmanager/data"}; -class DataSet : public testing::TestWithParam +class QmlProjectConverter : public testing::TestWithParam { public: void setDataSource(const QString &dataSetName) @@ -64,10 +64,12 @@ private: }; INSTANTIATE_TEST_SUITE_P(QmlProjectItem, - DataSet, - ::testing::Values(QString("test-set-1"), QString("test-set-2"))); + QmlProjectConverter, + ::testing::Values(QString("test-set-1"), + QString("test-set-2"), + QString("test-set-3"))); -TEST_P(DataSet, qml_project_to_json) +TEST_P(QmlProjectConverter, qml_project_to_json) { // GIVEN setDataSource(GetParam()); @@ -82,7 +84,7 @@ TEST_P(DataSet, qml_project_to_json) ASSERT_THAT(convertedContent, Eq(targetContent)); } -TEST_P(DataSet, json_to_qml_project) +TEST_P(QmlProjectConverter, json_to_qml_project) { // GIVEN setDataSource(GetParam()); diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/README.md b/tests/unit/tests/unittests/qmlprojectmanager/data/README.md index 0edd94edd93..bffd46b79ee 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/README.md +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/README.md @@ -25,6 +25,12 @@ Test functions iterate over the "test-set-*" folders and run the tests by using * **purpose**: testing fileselectors * **origin**: file selectors example from playground +### test-set-3 + +* **purpose**: testing `QDS.` prefixes +* **origin**: copy of test-set-1 + + ## File Filters test data Test data contains an example project folders that file filters will be initialized and tested. diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml new file mode 100644 index 00000000000..dbd6e4a91ac --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml @@ -0,0 +1,98 @@ +// prop: json-converted +// prop: auto-generated + +import QmlProject + +Project { + mainFile: "content/App.qml" + mainUiFile: "content/Screen01.ui.qml" + targetDirectory: "/opt/UntitledProject13" + widgetApp: true + importPaths: [ "imports","asset_imports" ] + + qdsVersion: "4.0" + quickVersion: "6.2" + qt6Project: true + qtForMCUs: true + + multilanguageSupport: true + primaryLanguage: "en" + supportedLanguages: [ "en" ] + + Environment { + QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT: "1" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QT_ENABLE_HIGHDPI_SCALING: "0" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + } + + ShaderTool { + args: "-s --glsl \"100 es,120,150\" --hlsl 50 --msl 12" + files: [ "content/shaders/*" ] + } + + QmlFiles { + directory: "content" + } + + QmlFiles { + directory: "imports" + } + + QmlFiles { + directory: "asset_imports" + } + + JavaScriptFiles { + directory: "content" + } + + JavaScriptFiles { + directory: "imports" + } + + ImageFiles { + directory: "content" + } + + ImageFiles { + directory: "asset_imports" + } + + Files { + directory: "." + filters: "*.conf" + files: [ "qtquickcontrols2.conf" ] + } + + Files { + directory: "." + filters: "*.ttf;*.otf;*.ctf" + } + + Files { + directory: "asset_imports" + filters: "*.mesh" + } + + Files { + directory: "." + filters: "qmldir" + } + + Files { + directory: "." + filters: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag;*.trag" + } + + Files { + directory: "." + filters: "*.mp3;*.wav" + } + + Files { + directory: "." + filters: "*.mp4" + } +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject new file mode 100644 index 00000000000..721dea3d28e --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject @@ -0,0 +1,112 @@ +import QmlProject + +Project { + QDS.mainFile: "content/App.qml" + QDS.mainUiFile: "content/Screen01.ui.qml" + + /* Include .qml, .js, and image files from current directory and subdirectories */ + QDS.QmlFiles { + directory: "content" + } + + QDS.QmlFiles { + directory: "imports" + } + + QDS.JavaScriptFiles { + directory: "content" + } + + QDS.JavaScriptFiles { + directory: "imports" + } + + QDS.ImageFiles { + directory: "content" + } + + QDS.ImageFiles { + directory: "asset_imports" + } + + QDS.Files { + filter: "*.conf" + files: ["qtquickcontrols2.conf"] + } + + QDS.Files { + filter: "qmldir" + directory: "." + } + + QDS.Files { + filter: "*.ttf;*.otf;*.ctf" + } + + QDS.Files { + filter: "*.wav;*.mp3" + } + + QDS.Files { + filter: "*.mp4" + } + + QDS.Files { + filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag;*.trag" + } + + QDS.Files { + filter: "*.mesh" + directory: "asset_imports" + } + + QDS.Files { + filter: "*.qml" + directory: "asset_imports" + } + + QDS.Environment { + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT: "1" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_ENABLE_HIGHDPI_SCALING: "0" + /* Useful for debugging + QSG_VISUALIZE=batches + QSG_VISUALIZE=clip + QSG_VISUALIZE=changes + QSG_VISUALIZE=overdraw + */ + } + + QDS.qt6Project: true + + /* List of plugin directories passed to QML runtime */ + QDS.importPaths: [ "imports", "asset_imports" ] + + /* Required for deployment */ + QDS.targetDirectory: "/opt/UntitledProject13" + + QDS.qdsVersion: "4.0" + + QDS.quickVersion: "6.2" + + QDS.qtForMCUs: true + + /* If any modules the project imports require widgets (e.g. QtCharts), widgetApp must be true */ + QDS.widgetApp: true + + /* args: Specifies command line arguments for qsb tool to generate shaders. + files: Specifies target files for qsb tool. If path is included, it must be relative to this file. + Wildcard '*' can be used in the file name part of the path. + e.g. files: [ "content/shaders/*.vert", "*.frag" ] */ + QDS.ShaderTool { + args: "-s --glsl \"100 es,120,150\" --hlsl 50 --msl 12" + files: [ "content/shaders/*" ] + } + + QDS.multilanguageSupport: true + QDS.supportedLanguages: ["en"] + QDS.primaryLanguage: "en" + +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson new file mode 100644 index 00000000000..293b8e96524 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson @@ -0,0 +1,178 @@ +{ + "deployment": { + "targetDirectory": "/opt/UntitledProject13" + }, + "environment": { + "QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT": "1", + "QT_AUTO_SCREEN_SCALE_FACTOR": "1", + "QT_ENABLE_HIGHDPI_SCALING": "0", + "QT_LOGGING_RULES": "qt.qml.connections=false", + "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" + }, + "fileGroups": { + "config": { + "directories": [ + "." + ], + "files": [ + { + "name": "qtquickcontrols2.conf" + } + ], + "filters": [ + "*.conf" + ] + }, + "font": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.ttf", + "*.otf", + "*.ctf" + ] + }, + "image": { + "directories": [ + "content", + "asset_imports" + ], + "files": [ + ], + "filters": [ + "*.jpeg", + "*.jpg", + "*.png", + "*.svg", + "*.hdr", + ".ktx" + ] + }, + "javaScript": { + "directories": [ + "content", + "imports" + ], + "files": [ + ], + "filters": [ + "*.js", + "*.ts" + ] + }, + "meshes": { + "directories": [ + "asset_imports" + ], + "files": [ + ], + "filters": [ + "*.mesh" + ] + }, + "qml": { + "directories": [ + "content", + "imports", + "asset_imports" + ], + "files": [ + ], + "filters": [ + "*.qml" + ] + }, + "qmldir": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "qmldir" + ] + }, + "shader": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.glsl", + "*.glslv", + "*.glslf", + "*.vsh", + "*.fsh", + "*.vert", + "*.frag", + "*.trag" + ] + }, + "sound": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.mp3", + "*.wav" + ] + }, + "video": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.mp4" + ] + } + }, + "fileVersion": 1, + "importPaths": [ + "imports", + "asset_imports" + ], + "language": { + "multiLanguageSupport": true, + "primaryLanguage": "en", + "supportedLanguages": [ + "en" + ] + }, + "mcuConfig": { + "mcuEnabled": true + }, + "runConfig": { + "fileSelectors": [ + ], + "mainFile": "content/App.qml", + "mainUiFile": "content/Screen01.ui.qml", + "widgetApp": true + }, + "shaderTool": { + "args": [ + "-s", + "--glsl", + "\"100 es,120,150\"", + "--hlsl", + "50", + "--msl", + "12" + ], + "files": [ + "content/shaders/*" + ] + }, + "versions": { + "designStudio": "4.0", + "qt": "6", + "qtQuick": "6.2" + } +} From 53e2115f63873679b1a6f0e62794edbb17c71a9b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 20 Sep 2023 11:05:22 +0200 Subject: [PATCH 085/130] QmlDesigner: Move the static function from ModeNode to ModelUtils ModelNode is already quite big and there is no need add more utility functions. Change-Id: I5e81c320c4934fc452c05f21b3c878354f857424 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../componentcore/designeractionmanager.cpp | 17 ++- .../connectioneditor/connectionmodel.cpp | 3 +- .../components/edit3d/edit3dwidget.cpp | 25 +++-- .../formeditor/abstractformeditortool.cpp | 4 +- .../components/navigator/nameitemdelegate.cpp | 5 +- .../navigator/navigatortreemodel.cpp | 6 +- .../timelineeditor/timelinesectionitem.cpp | 5 +- .../transitioneditorsectionitem.cpp | 5 +- .../designercore/include/modelnode.h | 3 - .../designercore/model/abstractview.cpp | 7 +- .../designercore/model/modelnode.cpp | 102 ----------------- .../designercore/model/modelutils.cpp | 105 ++++++++++++++++++ .../designercore/model/modelutils.h | 3 + 13 files changed, 150 insertions(+), 140 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 098376f0fe9..5239837ed49 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -23,10 +23,11 @@ #include -#include -#include -#include #include +#include +#include +#include +#include #include #include @@ -419,7 +420,7 @@ public: parentNode = selectionContext().currentSingleSelectedNode().parentProperty().parentModelNode(); - if (!ModelNode::isThisOrAncestorLocked(parentNode)) { + if (!ModelUtils::isThisOrAncestorLocked(parentNode)) { ActionTemplate *selectionAction = new ActionTemplate("SELECTION", {}, &ModelNodeOperations::select); selectionAction->setParent(menu()); selectionAction->setText(QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Parent"))); @@ -432,11 +433,9 @@ public: } } for (const ModelNode &node : selectionContext().view()->allModelNodes()) { - if (node != selectionContext().currentSingleSelectedNode() - && node != parentNode - && contains(node, selectionContext().scenePosition()) - && !node.isRootNode() - && !ModelNode::isThisOrAncestorLocked(node)) { + if (node != selectionContext().currentSingleSelectedNode() && node != parentNode + && contains(node, selectionContext().scenePosition()) && !node.isRootNode() + && !ModelUtils::isThisOrAncestorLocked(node)) { selectionContext().setTargetNode(node); QString what = QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select: %1")).arg(captionForModelNode(node)); ActionTemplate *selectionAction = new ActionTemplate("SELECT", what, &ModelNodeOperations::select); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 50ad727fe22..5791b8ad6c6 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -69,7 +70,7 @@ Qt::ItemFlags ConnectionModel::flags(const QModelIndex &modelIndex) const const int internalId = data(index(modelIndex.row(), TargetModelNodeRow), UserRoles::InternalIdRole).toInt(); ModelNode modelNode = m_connectionView->modelNodeForInternalId(internalId); - if (modelNode.isValid() && ModelNode::isThisOrAncestorLocked(modelNode)) + if (modelNode.isValid() && ModelUtils::isThisOrAncestorLocked(modelNode)) return Qt::ItemIsEnabled; return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index f330094b71c..c054b712ce3 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -44,7 +45,8 @@ namespace QmlDesigner { -static inline QIcon contextIcon(const DesignerIcons::IconId &iconId) { +inline static QIcon contextIcon(const DesignerIcons::IconId &iconId) +{ return DesignerActionManager::instance().contextIcon(iconId); }; @@ -257,17 +259,16 @@ void Edit3DWidget::createContextMenu() m_contextMenu->addSeparator(); m_selectParentAction = m_contextMenu->addAction( - contextIcon(DesignerIcons::ParentIcon), - tr("Select Parent"), [&] { - ModelNode parentNode = ModelNode::lowestCommonAncestor(view()->selectedModelNodes()); - if (!parentNode.isValid()) - return; + contextIcon(DesignerIcons::ParentIcon), tr("Select Parent"), [&] { + ModelNode parentNode = ModelUtils::lowestCommonAncestor(view()->selectedModelNodes()); + if (!parentNode.isValid()) + return; - if (!parentNode.isRootNode() && view()->isSelectedModelNode(parentNode)) - parentNode = parentNode.parentProperty().parentModelNode(); + if (!parentNode.isRootNode() && view()->isSelectedModelNode(parentNode)) + parentNode = parentNode.parentProperty().parentModelNode(); - view()->setSelectedModelNode(parentNode); - }); + view()->setSelectedModelNode(parentNode); + }); QAction *defaultToggleGroupAction = view()->edit3DAction(View3DActionType::SelectionModeToggle)->action(); m_toggleGroupAction = m_contextMenu->addAction( @@ -291,7 +292,7 @@ bool Edit3DWidget::isSceneLocked() const { if (m_view && m_view->hasModelNodeForInternalId(m_canvas->activeScene())) { ModelNode node = m_view->modelNodeForInternalId(m_canvas->activeScene()); - if (ModelNode::isThisOrAncestorLocked(node)) + if (ModelUtils::isThisOrAncestorLocked(node)) return true; } return false; @@ -525,7 +526,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) // Block all drags if scene root node is locked if (m_view->hasModelNodeForInternalId(m_canvas->activeScene())) { ModelNode node = m_view->modelNodeForInternalId(m_canvas->activeScene()); - if (ModelNode::isThisOrAncestorLocked(node)) + if (ModelUtils::isThisOrAncestorLocked(node)) return; } diff --git a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp index 774b4c0d911..657fb4773e0 100644 --- a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp @@ -9,6 +9,8 @@ #include "modelnodecontextmenu.h" #include "qmldesignerconstants.h" +#include + #include #include #include @@ -180,7 +182,7 @@ FormEditorItem* AbstractFormEditorTool::nearestFormEditorItem(const QPointF &poi if (formEditorItem->parentItem() && !formEditorItem->parentItem()->isContentVisible()) continue; - if (formEditorItem && ModelNode::isThisOrAncestorLocked(formEditorItem->qmlItemNode().modelNode())) + if (formEditorItem && ModelUtils::isThisOrAncestorLocked(formEditorItem->qmlItemNode().modelNode())) continue; if (!nearestItem) diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp index a955a81c0ee..1a3814f9618 100644 --- a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp +++ b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp @@ -12,10 +12,11 @@ #include "qproxystyle.h" #include +#include #include +#include #include #include -#include #include #include @@ -212,7 +213,7 @@ void NameItemDelegate::paint(QPainter *painter, } ModelNode node = getModelNode(modelIndex); - if (!ModelNode::isThisOrAncestorLocked(node)) { + if (!ModelUtils::isThisOrAncestorLocked(node)) { NavigatorWidget *widget = qobject_cast(styleOption.widget->parent()); if (widget && !widget->dragType().isEmpty()) { QByteArray dragType = widget->dragType(); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 8b7f1f8a61e..6abda00dbac 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -200,7 +200,7 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked; if (role == ItemOrAncestorLocked) - return ModelNode::isThisOrAncestorLocked(modelNode); + return ModelUtils::isThisOrAncestorLocked(modelNode); if (role == ModelNodeRole) return QVariant::fromValue(modelNode); @@ -273,13 +273,13 @@ Qt::ItemFlags NavigatorTreeModel::flags(const QModelIndex &index) const if (index.column() == ColumnType::Alias || index.column() == ColumnType::Visibility || index.column() == ColumnType::Lock) { - if (ModelNode::isThisOrAncestorLocked(modelNode)) + if (ModelUtils::isThisOrAncestorLocked(modelNode)) return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; else return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; } - if (ModelNode::isThisOrAncestorLocked(modelNode)) + if (ModelUtils::isThisOrAncestorLocked(modelNode)) return Qt::NoItemFlags; if (index.column() == ColumnType::Name) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp index 7a733c37291..b67c70bb0bc 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -301,7 +302,7 @@ void TimelineSectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) if (event->button() == Qt::LeftButton) { event->accept(); - if (!ModelNode::isThisOrAncestorLocked(m_targetNode)) + if (!ModelUtils::isThisOrAncestorLocked(m_targetNode)) toggleCollapsed(); } } @@ -334,7 +335,7 @@ void TimelineSectionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) if (m_targetNode.isValid()) m_targetNode.view()->setSelectedModelNode(m_targetNode); } else { - if (!ModelNode::isThisOrAncestorLocked(m_targetNode)) + if (!ModelUtils::isThisOrAncestorLocked(m_targetNode)) toggleCollapsed(); } update(); diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp index a5ec84fad2c..e12b6e915b6 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -349,7 +350,7 @@ void TransitionEditorSectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent if (event->button() == Qt::LeftButton) { event->accept(); - if (!ModelNode::isThisOrAncestorLocked(m_targetNode)) + if (!ModelUtils::isThisOrAncestorLocked(m_targetNode)) toggleCollapsed(); } } @@ -382,7 +383,7 @@ void TransitionEditorSectionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *ev if (m_targetNode.isValid()) m_targetNode.view()->setSelectedModelNode(m_targetNode); } else { - if (!ModelNode::isThisOrAncestorLocked(m_targetNode)) + if (!ModelUtils::isThisOrAncestorLocked(m_targetNode)) toggleCollapsed(); } update(); diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index 676a211cd61..0517599f5be 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -223,9 +223,6 @@ public: bool locked() const; void setLocked(bool value); - static bool isThisOrAncestorLocked(const ModelNode &node); - static ModelNode lowestCommonAncestor(const QList &nodes); - qint32 internalId() const; void setNodeSource(const QString&); diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 17cdffb6f7e..2a9e0e12462 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -8,12 +8,13 @@ #include "internalnode_p.h" #include "model.h" #include "model_p.h" +#include "modelutils.h" #include "nodeinstanceview.h" #include "nodelistproperty.h" #include "nodemetainfo.h" +#include "qmldesignerconstants.h" #include "qmlstate.h" #include "qmltimeline.h" -#include "qmldesignerconstants.h" #include "rewritertransaction.h" #include "variantproperty.h" @@ -412,7 +413,7 @@ void AbstractView::setSelectedModelNodes(const QList &selectedNodeLis QList unlockedNodes; for (const auto &modelNode : selectedNodeList) { - if (!ModelNode::isThisOrAncestorLocked(modelNode)) + if (!ModelUtils::isThisOrAncestorLocked(modelNode)) unlockedNodes.push_back(modelNode); } @@ -421,7 +422,7 @@ void AbstractView::setSelectedModelNodes(const QList &selectedNodeLis void AbstractView::setSelectedModelNode(const ModelNode &modelNode) { - if (ModelNode::isThisOrAncestorLocked(modelNode)) { + if (ModelUtils::isThisOrAncestorLocked(modelNode)) { clearSelectedModelNodes(); return; } diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index c285161e833..c8d9132d572 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -1230,108 +1230,6 @@ void ModelNode::setLocked(bool value) } } -bool ModelNode::isThisOrAncestorLocked(const ModelNode &node) -{ - if (!node.isValid()) - return false; - - if (node.locked()) - return true; - - if (node.isRootNode() || !node.hasParentProperty()) - return false; - - return isThisOrAncestorLocked(node.parentProperty().parentModelNode()); -} - -/*! - * \brief The lowest common ancestor node for node1 and node2. If one of the nodes (Node A) is - * the ancestor of the other node, the return value is Node A and not the parent of Node A. - * \param node1 First node - * \param node2 Second node - * \param depthOfLCA Depth of the return value - * \param depthOfNode1 Depth of node1. Use this parameter for optimization - * \param depthOfNode2 Depth of node2. Use this parameter for optimization - */ -static ModelNode lowestCommonAncestor(const ModelNode &node1, - const ModelNode &node2, - int &depthOfLCA, - const int &depthOfNode1 = -1, - const int &depthOfNode2 = -1) -{ - Q_ASSERT(node1.isValid() && node2.isValid()); - - auto depthOfNode = [](const ModelNode &node) -> int { - int depth = 0; - ModelNode parentNode = node; - while (!parentNode.isRootNode()) { - depth++; - parentNode = parentNode.parentProperty().parentModelNode(); - } - return depth; - }; - - if (node1 == node2) { - depthOfLCA = (depthOfNode1 < 0) ? ((depthOfNode2 < 0) ? depthOfNode(node1) : depthOfNode2) - : depthOfNode1; - return node1; - } - - if (node1.isRootNode()) { - depthOfLCA = 0; - return node1; - } - - if (node2.isRootNode()) { - depthOfLCA = 0; - return node2; - } - - ModelNode nodeLower = node1; - ModelNode nodeHigher = node2; - int depthLower = (depthOfNode1 < 0) ? depthOfNode(nodeLower) : depthOfNode1; - int depthHigher = (depthOfNode2 < 0) ? depthOfNode(nodeHigher) : depthOfNode2; - - if (depthLower > depthHigher) { - std::swap(depthLower, depthHigher); - std::swap(nodeLower, nodeHigher); - } - - int depthDiff = depthHigher - depthLower; - while (depthDiff--) - nodeHigher = nodeHigher.parentProperty().parentModelNode(); - - while (nodeLower != nodeHigher) { - nodeLower = nodeLower.parentProperty().parentModelNode(); - nodeHigher = nodeHigher.parentProperty().parentModelNode(); - --depthLower; - } - - depthOfLCA = depthLower; - return nodeLower; -} - -/*! - * \brief The lowest common node containing all nodes. If one of the nodes (Node A) is - * the ancestor of the other nodes, the return value is Node A and not the parent of Node A. - */ -ModelNode ModelNode::lowestCommonAncestor(const QList &nodes) -{ - if (nodes.isEmpty()) - return {}; - - ModelNode accumulatedNode = nodes.first(); - int accumulatedNodeDepth = -1; - for (const ModelNode &node : Utils::span(nodes).subspan(1)) { - accumulatedNode = QmlDesigner::lowestCommonAncestor(accumulatedNode, - node, - accumulatedNodeDepth, - accumulatedNodeDepth); - } - - return accumulatedNode; -} - void ModelNode::setScriptFunctions(const QStringList &scriptFunctionList) { if (!isValid()) diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp index e1fe3f3cadc..22f472c3e5f 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -4,6 +4,7 @@ #include "modelutils.h" #include +#include #include #include #include @@ -162,4 +163,108 @@ QList allModelNodesWithId(AbstractView *view) [&](const ModelNode &node) { return node.hasId(); }); } +bool isThisOrAncestorLocked(const ModelNode &node) +{ + if (!node.isValid()) + return false; + + if (node.locked()) + return true; + + if (node.isRootNode() || !node.hasParentProperty()) + return false; + + return isThisOrAncestorLocked(node.parentProperty().parentModelNode()); +} + +/*! + * \brief The lowest common ancestor node for node1 and node2. If one of the nodes (Node A) is + * the ancestor of the other node, the return value is Node A and not the parent of Node A. + * \param node1 First node + * \param node2 Second node + * \param depthOfLCA Depth of the return value + * \param depthOfNode1 Depth of node1. Use this parameter for optimization + * \param depthOfNode2 Depth of node2. Use this parameter for optimization + */ +namespace { +ModelNode lowestCommonAncestor(const ModelNode &node1, + const ModelNode &node2, + int &depthOfLCA, + const int &depthOfNode1 = -1, + const int &depthOfNode2 = -1) +{ + Q_ASSERT(node1.isValid() && node2.isValid()); + + auto depthOfNode = [](const ModelNode &node) -> int { + int depth = 0; + ModelNode parentNode = node; + while (!parentNode.isRootNode()) { + depth++; + parentNode = parentNode.parentProperty().parentModelNode(); + } + return depth; + }; + + if (node1 == node2) { + depthOfLCA = (depthOfNode1 < 0) ? ((depthOfNode2 < 0) ? depthOfNode(node1) : depthOfNode2) + : depthOfNode1; + return node1; + } + + if (node1.isRootNode()) { + depthOfLCA = 0; + return node1; + } + + if (node2.isRootNode()) { + depthOfLCA = 0; + return node2; + } + + ModelNode nodeLower = node1; + ModelNode nodeHigher = node2; + int depthLower = (depthOfNode1 < 0) ? depthOfNode(nodeLower) : depthOfNode1; + int depthHigher = (depthOfNode2 < 0) ? depthOfNode(nodeHigher) : depthOfNode2; + + if (depthLower > depthHigher) { + std::swap(depthLower, depthHigher); + std::swap(nodeLower, nodeHigher); + } + + int depthDiff = depthHigher - depthLower; + while (depthDiff--) + nodeHigher = nodeHigher.parentProperty().parentModelNode(); + + while (nodeLower != nodeHigher) { + nodeLower = nodeLower.parentProperty().parentModelNode(); + nodeHigher = nodeHigher.parentProperty().parentModelNode(); + --depthLower; + } + + depthOfLCA = depthLower; + return nodeLower; +} +} // namespace + +/*! + * \brief The lowest common node containing all nodes. If one of the nodes (Node A) is + * the ancestor of the other nodes, the return value is Node A and not the parent of Node A. + */ +ModelNode lowestCommonAncestor(const QList &nodes) +{ + if (nodes.isEmpty()) + return {}; + + ModelNode accumulatedNode = nodes.first(); + int accumulatedNodeDepth = -1; + for (const ModelNode &node : Utils::span(nodes).subspan(1)) { + accumulatedNode = lowestCommonAncestor(accumulatedNode, + node, + accumulatedNodeDepth, + accumulatedNodeDepth); + } + + return accumulatedNode; +} + } // namespace QmlDesigner::ModelUtils diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.h b/src/plugins/qmldesigner/designercore/model/modelutils.h index ee6d3e41303..26cf29aac00 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.h +++ b/src/plugins/qmldesigner/designercore/model/modelutils.h @@ -38,4 +38,7 @@ QMLDESIGNERCORE_EXPORT QList pruneChildren(const QList &no QMLDESIGNERCORE_EXPORT QList allModelNodesWithId(AbstractView *view); +QMLDESIGNERCORE_EXPORT bool isThisOrAncestorLocked(const ModelNode &node); +QMLDESIGNERCORE_EXPORT ModelNode lowestCommonAncestor(const QList &nodes); + } // namespace QmlDesigner::ModelUtils From 67605661dfcb2fda9c34b82b05397a2ac3902045 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Mon, 25 Sep 2023 16:55:03 +0200 Subject: [PATCH 086/130] QmlDesigner: Bump MCUs version Task-number: QDS-10768 Change-Id: I8033319eee6a5a48c02d4883a739b2dac86c521e Reviewed-by: Thomas Hartmann --- .../qtcreator/qmldesigner/qt4mcu/metadata.qml | 14 +- share/qtcreator/qmldesigner/qt4mcu/qul-17.qml | 2 + share/qtcreator/qmldesigner/qt4mcu/qul-18.qml | 5 + share/qtcreator/qmldesigner/qt4mcu/qul-21.qml | 2 + share/qtcreator/qmldesigner/qt4mcu/qul-22.qml | 2 + share/qtcreator/qmldesigner/qt4mcu/qul-23.qml | 3 + share/qtcreator/qmldesigner/qt4mcu/qul-24.qml | 3 + share/qtcreator/qmldesigner/qt4mcu/qul-25.qml | 217 ++++++++++++++++++ share/qtcreator/qmldesigner/qt4mcu/qul-26.qml | 2 + .../application-mcu/app_mcu.qmlproject.tpl | 2 +- 10 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 share/qtcreator/qmldesigner/qt4mcu/qul-25.qml diff --git a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml index d4da42cc0a1..d208ee096ac 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml @@ -5,7 +5,7 @@ Metadata { id: metadataFile - defaultVersion: v24 + defaultVersion: v25 VersionData { id: v14 @@ -60,4 +60,16 @@ Metadata { name: "Qt for MCUs 2.4" path: "qul-24.qml" } + + VersionData { + id: v25 + name: "Qt for MCUs 2.5" + path: "qul-25.qml" + } + + VersionData { + id: v26 + name: "Qt for MCUs 2.6" + path: "qul-26.qml" + } } diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-17.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-17.qml index 5a29a3b7e62..b41e9794ac6 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-17.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-17.qml @@ -1,6 +1,8 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// new MCU-specific imports: QtQuickUltralite.Extras, QtQuickUltralite.Layers + VersionData { name: "Qt for MCUs 1.7" diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-18.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-18.qml index 8597cfd9320..8a74250cab1 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-18.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-18.qml @@ -1,6 +1,11 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// new import: QtQuick.Shapes +// new import provides new types: QtQuick.Shapes.Shape, QtQuick.Shapes.ShapePath +// new types: QtQuick.Path, PathArc, PathLine, PathMove, PathQuad, PathCubic, PathElement, PathSvg + + VersionData { name: "Qt for MCUs 1.8" diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml index 47d416ba9ee..bbbdcffd177 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml @@ -1,6 +1,8 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// new property: QtQuick.Text::elide + VersionData { name: "Qt for MCUs 2.1" diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml index 9af17175b49..82ec865cdde 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml @@ -1,6 +1,8 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// new properties: QtQuick.Text::wrapMode, QtQuick.Controls.AbstractButton::icon + VersionData { name: "Qt for MCUs 2.2" diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-23.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-23.qml index 867790a6542..934a8229a25 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-23.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-23.qml @@ -1,6 +1,9 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// new type: QtQuick.Loader +// new properties: QtQuick.Flickable::boundsBehavior, ::flickableDirection + VersionData { name: "Qt for MCUs 2.3" diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml index 0475203251a..dbeeabf971d 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml @@ -1,6 +1,9 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// new type: QtQuick.AnimatedSprite +// new property: QtQuick.Loader::sourceComponent + VersionData { name: "Qt for MCUs 2.4" diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-25.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-25.qml new file mode 100644 index 00000000000..3fb7e1b9259 --- /dev/null +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-25.qml @@ -0,0 +1,217 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +VersionData { + name: "Qt for MCUs 2.5" + + bannedItems: [ + "QtQuick.AnimatedImage", + "QtQuick.Flow", + "QtQuick.FocusScope", + "QtQuick.Grid", + "QtQuick.GridView", + "QtQuick.PathView", + "QtQuick.TextEdit", + "QtQuick.TextInput", + "QtQuick.Controls", + "QtQuick.Controls.BusyIndicator", + "QtQuick.Controls.ButtonGroup", + "QtQuick.Controls.CheckDelegate", + "QtQuick.Controls.ComboBox", + "QtQuick.Controls.Container", + "QtQuick.Controls.DelayButton", + "QtQuick.Controls.Frame", + "QtQuick.Controls.GroupBox", + "QtQuick.Controls.ItemDelegate", + "QtQuick.Controls.Label", + "QtQuick.Controls.Page", + "QtQuick.Controls.PageIndicator", + "QtQuick.Controls.Pane", + "QtQuick.Controls.RadioDelegate", + "QtQuick.Controls.RangeSlider", + "QtQuick.Controls.RoundButton", + "QtQuick.Controls.ScrollView", + "QtQuick.Controls.SpinBox", + "QtQuick.Controls.StackView", + "QtQuick.Controls.SwipeDelegate", + "QtQuick.Controls.SwitchDelegate", + "QtQuick.Controls.TabBar", + "QtQuick.Controls.TabButton", + "QtQuick.Controls.TextArea", + "QtQuick.Controls.TextField", + "QtQuick.Controls.ToolBar", + "QtQuick.Controls.ToolButton", + "QtQuick.Controls.ToolSeparator", + "QtQuick.Controls.Tumbler", + "QtQuick.Shapes.ConicalGradient", + "QtQuick.Shapes.LinearGradient", + "QtQuick.Shapes.RadialGradient", + "QtQuick.Shapes.ShapeGradient" + ] + + allowedImports: [ + "QtQuick", + "QtQuick.Controls", + "QtQuick.Shapes", + "QtQuick.Timeline", + "QtQuickUltralite.Extras", + "QtQuickUltralite.Layers" + ] + + bannedImports: [ + "FlowView", + "SimulinkConnector" + ] + + //ComplexProperty is not a type, it's just a way to handle bigger props + ComplexProperty { + prefix: "font" + bannedProperties: ["wordSpacing", "letterSpacing", "hintingPreference", + "kerning", "preferShaping", "capitalization", + "strikeout", "underline", "styleName"] + } + + QtQml.Timer { + bannedProperties: ["triggeredOnStart"] + } + + QtQuick.Item { + bannedProperties: ["layer", "opacity", "smooth", "antialiasing", + "baselineOffset", "focus", "activeFocusOnTab", + "rotation", "scale", "transformOrigin"] + } + + QtQuick.Rectangle { + bannedProperties: ["gradient", "border"] + } + + QtQuick.Flickable { + bannedProperties: ["boundsMovement", "flickDeceleration", + "leftMargin", "rightMargin", "bottomMargin", "topMargin", + "originX", "originY", "pixelAligned", "pressDelay", "synchronousDrag"] + } + + QtQuick.MouseArea { + bannedProperties: ["propagateComposedEvents", "preventStealing", "cursorShape", + "scrollGestureEnabled", "drag", "acceptedButtons", "hoverEnabled"] + } + + QtQuick.Image { + allowChildren: false + allowedProperties: ["rotation", "scale", "transformOrigin"] + bannedProperties: ["mirror", "mipmap", "cache", "autoTransform", "asynchronous", + "sourceSize", "smooth"] + } + + QtQuick.BorderImage { + bannedProperties: ["asynchronous", "cache", "currentFrame", "frameCount", + "horizontalTileMode", "mirror", "progress", "smooth", "sourceSize", + "status", "verticalTileMode"] + } + + QtQuick.Text { + allowChildren: false + allowedProperties: ["rotation", "scale", "transformOrigin"] + bannedProperties: ["lineHeight", "lineHeightMode", "style", + "styleColor", "minimumPointSize", "minimumPixelSize", + "fontSizeMode", "renderType", "renderTypeQuality", "textFormat", "maximumLineCount"] + } + + QtQuick.Loader { + bannedProperties: ["asynchronous", "progress", "status"] + } + + //Padding is not an actual item, but rather set of properties in Text + Padding { + bannedProperties: ["bottomPadding", "topPadding", "leftPadding", "rightPadding"] + } + + QtQuick.Column { + bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding"] + } + + QtQuick.Row { + bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding", + "effectiveLayoutDirection", "layoutDirection"] + } + + QtQuick.ListView { + bannedProperties: ["cacheBuffer", "highlightRangeMode", "highlightMoveDuration", + "highlightResizeDuration", "preferredHighlightBegin", "layoutDirection", + "preferredHighlightEnd", "highlightFollowsCurrentItem", "keyNavigationWraps", + "snapMode", "highlightMoveVelocity", "highlightResizeVelocity"] + } + + QtQuick.Animation { + bannedProperties: ["paused"] + } + + //Quick Controls2 Items and properties: + + QtQuick.Controls.Control { + bannedProperties: ["focusPolicy", "hoverEnabled", "wheelEnabled"] + } + + QtQuick.Controls.AbstractButton { + bannedProperties: ["display", "autoExclusive", "icon"] + } + + QtQuick.Controls.ProgressBar { + bannedProperties: ["indeterminate"] + } + + QtQuick.Controls.Slider { + bannedProperties: ["live", "snapMode", "touchDragThreshold"] + } + + //Path and Shapes related: + + QtQuick.Path { + bannedProperties: ["scale", "pathElements"] + } + + QtQuick.PathArc { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathLine { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathMove { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathQuad { + bannedProperties: ["relativeX", "relativeY", + "relativeControlX", "relativeControlY"] + } + + QtQuick.PathCubic { + bannedProperties: ["relativeX", "relativeY", + "relativeControl1X", "relativeControl1Y", + "relativeControl2X", "relativeControl2Y"] + } + + QtQuick.PathElement { + //nothing + } + + QtQuick.PathSvg { + //nothing + } + + QtQuick.Shapes.Shape { + bannedProperties: ["asynchronous", "containsMode", "data", + "renderType", "status", "vendorExtensionsEnabled"] + } + + QtQuick.Shapes.ShapePath { + bannedProperties: ["dashOffset", "dashPattern", + "fillGradient", "strokeStyle"] + } + + QtQuickUltralite.Extras.ItemBuffer { + allowedProperties: ["rotation", "scale", "transformOrigin"] + } +} diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml index cc9235ed63c..26c68a6e7d4 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml @@ -1,6 +1,8 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// new property: QtQuick.Text::textFormat + VersionData { name: "Qt for MCUs 2.6" diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl index 80bb0c5743e..f17d608f96c 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl @@ -55,7 +55,7 @@ Project { QDS.qtForMCUs: true QDS.qt6Project: true - QDS.qdsVersion: "4.2" + QDS.qdsVersion: "4.3" QDS.quickVersion: "6.5" /* List of plugin directories passed to QML runtime */ From 8e0e097f559abd5322be674763b3baa29080c818 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Fri, 22 Sep 2023 12:29:40 +0200 Subject: [PATCH 087/130] QmlProjectManager: use qmlpuppet as qmlRunCommand only at host Change-Id: I7a8e33bdf1007f26e45514abde378ca9b6799c81 Reviewed-by: Tim Jenssen --- .../qmlprojectmanager/qmlprojectrunconfiguration.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index 17f44537a15..3678e789e13 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -213,13 +214,17 @@ FilePath QmlProjectRunConfiguration::qmlRuntimeFilePath() const if (!qmlRuntime.isEmpty()) return qmlRuntime; } + auto hasDeployStep = [this]() { + return target()->activeDeployConfiguration() && + !target()->activeDeployConfiguration()->stepList()->isEmpty(); + }; // The Qt version might know, but we need to make sure // that the device can reach it. if (QtVersion *version = QtKitAspect::qtVersion(kit)) { // look for puppet as qmlruntime only in QtStudio Qt versions if (version->features().contains("QtStudio") && - version->qtVersion().majorVersion() > 5) { + version->qtVersion().majorVersion() > 5 && !hasDeployStep()) { auto [workingDirectoryPath, puppetPath] = QmlDesigner::QmlPuppetPaths::qmlPuppetPaths( target(), QmlDesigner::QmlDesignerBasePlugin::settings()); From 3612039365e3bd7f2e714c1f61d2665c2fbbd315 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 22 Sep 2023 17:09:04 +0200 Subject: [PATCH 088/130] QmlDesigner: Remove smooth from image specifics Remove the smooth property from image specifics as it is also available in the advanced section. Task-number: QDS-8726 Change-Id: I842a7978e65d4578434b158340484649abe4956f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Pranta Ghosh Dastider Reviewed-by: Thomas Hartmann --- .../QtQuick/AdvancedSection.qml | 2 +- .../QtQuick/BorderImageSpecifics.qml | 18 ------------------ .../imports/HelperWidgets/ImageSection.qml | 18 ------------------ 3 files changed, 1 insertion(+), 37 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml index e9be2285c52..f88defbff56 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml @@ -33,7 +33,7 @@ Section { PropertyLabel { text: qsTr("Smooth") - tooltip: qsTr("Uses smooth filtering when the image is scaled or transformed.") + tooltip: qsTr("Toggles if the smoothing is performed using linear interpolation method. Keeping it unchecked would follow non-smooth method using nearest neighbor. It is mostly applicable on image based items. ") blockedByTemplate: !backendValues.smooth.isAvailable } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml index 0862b7e6d87..63f3906658d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml @@ -216,24 +216,6 @@ Column { ExpandingSpacer {} } - PropertyLabel { - text: qsTr("Smooth") - tooltip: qsTr("Toggles if the image should be filtered smoothly when transformed.") - blockedByTemplate: !backendValues.smooth.isAvailable - } - - SecondColumnLayout { - CheckBox { - text: backendValues.smooth.valueToString - implicitWidth: StudioTheme.Values.twoControlColumnWidth - + StudioTheme.Values.actionIndicatorWidth - backendValue: backendValues.smooth - enabled: backendValue.isAvailable - } - - ExpandingSpacer {} - } - PropertyLabel { text: qsTr("Cache") tooltip: qsTr("Toggles if the image is saved to the cache memory.") diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImageSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImageSection.qml index 520a47f0ba3..d0ce0a109ce 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImageSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImageSection.qml @@ -224,23 +224,5 @@ Section { ExpandingSpacer {} } - - PropertyLabel { - text: qsTr("Smooth") - tooltip: qsTr("Uses smooth filtering when the image is scaled or transformed.") - blockedByTemplate: !backendValues.smooth.isAvailable - } - - SecondColumnLayout { - CheckBox { - text: backendValues.smooth.valueToString - implicitWidth: StudioTheme.Values.twoControlColumnWidth - + StudioTheme.Values.actionIndicatorWidth - backendValue: backendValues.smooth - enabled: backendValue.isAvailable - } - - ExpandingSpacer {} - } } } From a63aa6b130b1befe43c51102b98e85bb96e99e32 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 26 Sep 2023 14:56:06 +0200 Subject: [PATCH 089/130] QmlDesigner: Change empty property view message Task-number: QDS-7861 Change-Id: Iefc9e880ae20e40fa75ea584d24a39bee332927d Reviewed-by: Mats Honkamaa --- .../qmldesigner/propertyEditorQmlSources/QtQuick/emptyPane.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/emptyPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/emptyPane.qml index e0927b9ba7b..a5dc28f6f86 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/emptyPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/emptyPane.qml @@ -19,7 +19,7 @@ Rectangle { anchors.fill: parent Controls.Label { - text: qsTr("Select a component in the 2D, Navigator, or Code view to see its properties.") + text: qsTr("Select a component to see its properties.") font.pixelSize: StudioTheme.Values.myFontSize * 1.5 color: StudioTheme.Values.themeTextColor wrapMode: Text.WordWrap From 1cee5e08b113b82311d72a2533826557c8f573cc Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 26 Sep 2023 14:18:34 +0200 Subject: [PATCH 090/130] QmlDesigner: Rename inherited font caption Task-number: QDS-8680 Change-Id: Ib40d4d2b547a7f04c9965cecb487ecfa5edda1a5 Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../QtQuick/Controls/FrameSpecifics.qml | 2 +- .../propertyEditorQmlSources/QtQuick/Controls/PaneSpecifics.qml | 2 +- .../QtQuick/Controls/ScrollViewSpecifics.qml | 2 +- .../QtQuick/Controls/StackViewSpecifics.qml | 2 +- .../QtQuick/Controls/SwipeViewSpecifics.qml | 2 +- .../QtQuick/Controls/ToolBarSpecifics.qml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/FrameSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/FrameSpecifics.qml index 8b2728bcff8..3b490548b6b 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/FrameSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/FrameSpecifics.qml @@ -20,7 +20,7 @@ Column { InsetSection {} FontSection { - caption: qsTr("Font Inheritance") + caption: qsTr("Font") expanded: false } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PaneSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PaneSpecifics.qml index 899e1e6b56c..7f08a3d7fd5 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PaneSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PaneSpecifics.qml @@ -18,7 +18,7 @@ Column { InsetSection {} FontSection { - caption: qsTr("Font Inheritance") + caption: qsTr("Font") expanded: false } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ScrollViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ScrollViewSpecifics.qml index 9dc06b7969b..1ca84213c6f 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ScrollViewSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ScrollViewSpecifics.qml @@ -72,7 +72,7 @@ Column { InsetSection {} FontSection { - caption: qsTr("Font Inheritance") + caption: qsTr("Font") expanded: false } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/StackViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/StackViewSpecifics.qml index 427c838da51..eadcea60de0 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/StackViewSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/StackViewSpecifics.qml @@ -16,7 +16,7 @@ Column { InsetSection {} FontSection { - caption: qsTr("Font Inheritance") + caption: qsTr("Font") expanded: false } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SwipeViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SwipeViewSpecifics.qml index d5a300223db..8051dc8af15 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SwipeViewSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SwipeViewSpecifics.qml @@ -59,7 +59,7 @@ Column { InsetSection {} FontSection { - caption: qsTr("Font Inheritance") + caption: qsTr("Font") expanded: false } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ToolBarSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ToolBarSpecifics.qml index 8c0388cb89c..17c05a62560 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ToolBarSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ToolBarSpecifics.qml @@ -43,7 +43,7 @@ Column { InsetSection {} FontSection { - caption: qsTr("Font Inheritance") + caption: qsTr("Font") expanded: false } } From 8f69e5c0f28462b04dbd30447fc8622b9154f332 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 26 Sep 2023 14:44:27 +0200 Subject: [PATCH 091/130] QmlDesigner: Force hide scrollbar in certain mode Force hide vertical scrollbar in landscape mode. Force hide horizontal scrollbar in portrait mode. Task-number: QDS-7850 Change-Id: I1c1d4c879a58309a61ceb646a6aeaccc2300f243 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- share/qtcreator/qmldesigner/stateseditor/Main.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/qtcreator/qmldesigner/stateseditor/Main.qml b/share/qtcreator/qmldesigner/stateseditor/Main.qml index 7126a327eaf..1e9c3dccde0 100644 --- a/share/qtcreator/qmldesigner/stateseditor/Main.qml +++ b/share/qtcreator/qmldesigner/stateseditor/Main.qml @@ -587,6 +587,7 @@ Rectangle { y: scrollView.height - height width: scrollView.availableWidth orientation: Qt.Horizontal + visible: root.isLandscape show: (scrollView.hovered || scrollView.focus || scrollView.adsFocus) && horizontalBar.isNeeded @@ -601,6 +602,7 @@ Rectangle { y: scrollView.topPadding height: scrollView.availableHeight orientation: Qt.Vertical + visible: !root.isLandscape show: (scrollView.hovered || scrollView.focus || scrollView.adsFocus) && verticalBar.isNeeded From 8da35ac06d70aa12f20e02535542a654a468a9ca Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 26 Sep 2023 15:27:11 +0300 Subject: [PATCH 092/130] QmlDesigner: Show snap interval on drag tooltip in 3D view Fixes: QDS-10751 Change-Id: I7c13075ae726c1f33992ec52c554491a21f7e5c1 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../qml2puppet/mockfiles/qt6/EditView3D.qml | 96 ++++++++++++------- .../qml2puppet/editor3d/generalhelper.cpp | 36 +++++++ .../qml2puppet/editor3d/generalhelper.h | 7 ++ .../qt5informationnodeinstanceserver.cpp | 25 +++-- 4 files changed, 125 insertions(+), 39 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 7cd8dd7f4a3..71c5e4d81e7 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -727,6 +727,11 @@ Item { } } } + function onUpdateDragTooltip() + { + gizmoLabel.updateLabel(); + rotateGizmoLabel.updateLabel(); + } } Node { @@ -834,6 +839,7 @@ Item { else viewRoot.changeObjectProperty([viewRoot.selectedNode], propertyNames); } + onCurrentAngleChanged: rotateGizmoLabel.updateLabel() } LightGizmo { @@ -1012,6 +1018,27 @@ Item { visible: targetNode.dragging z: 3 + function updateLabel() + { + // This is skipped during application shutdown, as calling QQuickText::setText() + // during application shutdown can crash the application. + if (!gizmoLabel.visible || !viewRoot.selectedNode || shuttingDown) + return; + var targetProperty; + if (gizmoLabel.targetNode === moveGizmo) + gizmoLabelText.text = _generalHelper.snapPositionDragTooltip(viewRoot.selectedNode.position); + else + gizmoLabelText.text = _generalHelper.snapScaleDragTooltip(viewRoot.selectedNode.scale); + } + + Connections { + target: viewRoot.selectedNode + function onPositionChanged() { gizmoLabel.updateLabel() } + function onScaleChanged() { gizmoLabel.updateLabel() } + } + + onVisibleChanged: gizmoLabel.updateLabel() + Rectangle { color: "white" x: -width / 2 @@ -1021,25 +1048,6 @@ Item { border.width: 1 Text { id: gizmoLabelText - text: { - // This is skipped during application shutdown, as calling QQuickText::setText() - // during application shutdown can crash the application. - if (shuttingDown) - return text; - var l = Qt.locale(); - var targetProperty; - if (viewRoot.selectedNode) { - if (gizmoLabel.targetNode === moveGizmo) - targetProperty = viewRoot.selectedNode.position; - else - targetProperty = viewRoot.selectedNode.scale; - return qsTr("x:") + Number(targetProperty.x).toLocaleString(l, 'f', 1) - + qsTr(" y:") + Number(targetProperty.y).toLocaleString(l, 'f', 1) - + qsTr(" z:") + Number(targetProperty.z).toLocaleString(l, 'f', 1); - } else { - return ""; - } - } anchors.centerIn: parent } } @@ -1057,21 +1065,19 @@ Item { parent: rotateGizmo.view3D z: 3 + function updateLabel() { + // This is skipped during application shutdown, as calling QQuickText::setText() + // during application shutdown can crash the application. + if (!rotateGizmoLabel.visible || !rotateGizmo.targetNode || shuttingDown) + return; + var degrees = rotateGizmo.currentAngle * (180 / Math.PI); + rotateGizmoLabelText.text = _generalHelper.snapRotationDragTooltip(degrees); + } + + onVisibleChanged: rotateGizmoLabel.updateLabel() + Text { id: rotateGizmoLabelText - text: { - // This is skipped during application shutdown, as calling QQuickText::setText() - // during application shutdown can crash the application. - if (shuttingDown) - return text; - var l = Qt.locale(); - if (rotateGizmo.targetNode) { - var degrees = rotateGizmo.currentAngle * (180 / Math.PI); - return Number(degrees).toLocaleString(l, 'f', 1); - } else { - return ""; - } - } anchors.centerIn: parent } } @@ -1135,4 +1141,30 @@ Item { visible: viewRoot.fps > 0 } } + + Keys.onPressed: (event) => { + switch (event.key) { + case Qt.Key_Control: + case Qt.Key_Shift: + gizmoLabel.updateLabel(); + rotateGizmoLabel.updateLabel(); + break; + default: + break; + } + event.accepted = false; + } + + Keys.onReleased: (event) => { + switch (event.key) { + case Qt.Key_Control: + case Qt.Key_Shift: + gizmoLabel.updateLabel(); + rotateGizmoLabel.updateLabel(); + break; + default: + break; + } + event.accepted = false; + } } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index e39113d774b..86267874110 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -962,6 +962,42 @@ void GeneralHelper::setSnapPositionInterval(double interval) } } +QString GeneralHelper::formatVectorDragTooltip(const QVector3D &vec, const QString &suffix) const +{ + return QObject::tr("x:%L1 y:%L2 z:%L3%L4") + .arg(vec.x(), 0, 'f', 1).arg(vec.y(), 0, 'f', 1) + .arg(vec.z(), 0, 'f', 1).arg(suffix); +} + +QString GeneralHelper::formatSnapStr(bool snapEnabled, double increment, const QString &suffix) const +{ + double inc = increment; + QString snapStr; + if (queryKeyboardForSnapping(snapEnabled, inc)) { + int precision = 0; + // We can have fractional snap if shift is pressed, so adjust precision in those cases + if (qRound(inc * 10.) != qRound(inc) * 10) + precision = 1; + snapStr = QObject::tr(" (Snap: %1%2)").arg(inc, 0, 'f', precision).arg(suffix); + } + return snapStr; +} + +QString GeneralHelper::snapPositionDragTooltip(const QVector3D &pos) const +{ + return formatVectorDragTooltip(pos, formatSnapStr(m_snapPosition, m_snapPositionInterval, {})); +} + +QString GeneralHelper::snapRotationDragTooltip(double angle) const +{ + return tr("%L1%L2").arg(angle, 0, 'f', 1).arg(formatSnapStr(m_snapRotation, m_snapRotationInterval, {})); +} + +QString GeneralHelper::snapScaleDragTooltip(const QVector3D &scale) const +{ + return formatVectorDragTooltip(scale, formatSnapStr(m_snapScale, m_snapScaleInterval * 100., tr("%"))); +} + double GeneralHelper::minGridStep() const { // Minimum grid step is a multiple of snap interval, as the last step is divided to subgrid diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 8daa10d2ce6..e44c2c60ce9 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -119,6 +119,10 @@ public: void setSnapRotationInterval(double interval) { m_snapRotationInterval = interval; } void setSnapScaleInterval(double interval) { m_snapScaleInterval = interval / 100.; } + Q_INVOKABLE QString snapPositionDragTooltip(const QVector3D &pos) const; + Q_INVOKABLE QString snapRotationDragTooltip(double angle) const; + Q_INVOKABLE QString snapScaleDragTooltip(const QVector3D &scale) const; + double minGridStep() const; void setBgColor(const QVariant &colors); @@ -132,12 +136,15 @@ signals: void rotationBlocksChanged(); void bgColorChanged(); void minGridStepChanged(); + void updateDragTooltip(); private: void handlePendingToolStateUpdate(); QVector3D pivotScenePosition(QQuick3DNode *node) const; bool getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVector3D &minBounds, QVector3D &maxBounds); + QString formatVectorDragTooltip(const QVector3D &vec, const QString &suffix) const; + QString formatSnapStr(bool snapEnabled, double increment, const QString &suffix) const; QTimer m_overlayUpdateTimer; QTimer m_toolStateUpdateTimer; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 3491aa85a02..ea052aa426c 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -344,22 +344,33 @@ void Qt5InformationNodeInstanceServer::updateSnapSettings( #ifdef QUICK3D_MODULE auto helper = qobject_cast(m_3dHelper); if (helper) { + bool changed = false; for (const auto &container : valueChanges) { - if (container.name() == "snapPos3d") + if (container.name() == "snapPos3d") { helper->setSnapPosition(container.value().toBool()); - else if (container.name() == "snapPosInt3d") + changed = true; + } else if (container.name() == "snapPosInt3d") { helper->setSnapPositionInterval(container.value().toDouble()); - else if (container.name() == "snapRot3d") + changed = true; + } else if (container.name() == "snapRot3d") { helper->setSnapRotation(container.value().toBool()); - else if (container.name() == "snapRotInt3d") + changed = true; + } else if (container.name() == "snapRotInt3d") { helper->setSnapRotationInterval(container.value().toDouble()); - else if (container.name() == "snapScale3d") + changed = true; + } else if (container.name() == "snapScale3d") { helper->setSnapScale(container.value().toBool()); - else if (container.name() == "snapScaleInt3d") + changed = true; + } else if (container.name() == "snapScaleInt3d") { helper->setSnapScaleInterval(container.value().toDouble()); - else if (container.name() == "snapAbs3d") + changed = true; + } else if (container.name() == "snapAbs3d") { helper->setSnapAbsolute(container.value().toBool()); + changed = true; + } } + if (changed) + emit helper->updateDragTooltip(); } #endif } From 5f20ccfde7fa189009de9921fa31637fbcd7b834 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 20 Sep 2023 17:02:28 +0200 Subject: [PATCH 093/130] QmlDesigner: Don't use view in model node Change-Id: Ic4d41a975355f4b386de50622fe2ca6ec217c97e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../designercore/model/modelnode.cpp | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index c8d9132d572..fe973f04abd 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -745,7 +745,7 @@ bool ModelNode::isRootNode() const if (!isValid()) return false; - return view()->rootModelNode() == *this; + return m_model->d->rootNode() == m_internalNode; } /*! \brief returns the list of all property names @@ -1143,7 +1143,7 @@ void ModelNode::removeAnnotation() Annotation ModelNode::globalAnnotation() const { Annotation result; - ModelNode root = view()->rootModelNode(); + ModelNode root = m_model->rootModelNode(); auto data = root.auxiliaryData(globalAnnotationProperty); @@ -1155,24 +1155,24 @@ Annotation ModelNode::globalAnnotation() const bool ModelNode::hasGlobalAnnotation() const { - return view()->rootModelNode().hasAuxiliaryData(globalAnnotationProperty); + return m_model->rootModelNode().hasAuxiliaryData(globalAnnotationProperty); } void ModelNode::setGlobalAnnotation(const Annotation &annotation) { - view()->rootModelNode().setAuxiliaryData(globalAnnotationProperty, - QVariant::fromValue(annotation.toQString())); + m_model->rootModelNode().setAuxiliaryData(globalAnnotationProperty, + QVariant::fromValue(annotation.toQString())); } void ModelNode::removeGlobalAnnotation() { - view()->rootModelNode().removeAuxiliaryData(globalAnnotationProperty); + m_model->rootModelNode().removeAuxiliaryData(globalAnnotationProperty); } GlobalAnnotationStatus ModelNode::globalStatus() const { GlobalAnnotationStatus result; - ModelNode root = view()->rootModelNode(); + ModelNode root = m_model->rootModelNode(); auto data = root.auxiliaryData(globalAnnotationStatus); @@ -1184,19 +1184,19 @@ GlobalAnnotationStatus ModelNode::globalStatus() const bool ModelNode::hasGlobalStatus() const { - return view()->rootModelNode().hasAuxiliaryData(globalAnnotationStatus); + return m_model->rootModelNode().hasAuxiliaryData(globalAnnotationStatus); } void ModelNode::setGlobalStatus(const GlobalAnnotationStatus &status) { - view()->rootModelNode().setAuxiliaryData(globalAnnotationStatus, - QVariant::fromValue(status.toQString())); + m_model->rootModelNode().setAuxiliaryData(globalAnnotationStatus, + QVariant::fromValue(status.toQString())); } void ModelNode::removeGlobalStatus() { if (hasGlobalStatus()) { - view()->rootModelNode().removeAuxiliaryData(globalAnnotationStatus); + m_model->rootModelNode().removeAuxiliaryData(globalAnnotationStatus); } } From d5f66a07e2a36563a5c3a5395e36bd6c587ba0ce Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 26 Sep 2023 17:40:57 +0200 Subject: [PATCH 094/130] QmlDesigner: Fix build for project storage Change-Id: Idd2285f7266dde85110af200e8b0d046edaa34ee Reviewed-by: Aleksei German Reviewed-by: Burak Hancerli --- .../qmldesigner/components/collectioneditor/collectionview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index aaf2605b64f..2f46dcbf699 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -584,7 +584,7 @@ void CollectionView::ensureCollectionLibraryNode() // Create collection library node #ifdef QDS_USE_PROJECTSTORAGE TypeName nodeTypeName = rootModelNode().metaInfo().isQtQuick3DNode() ? "Node" : "Item"; - matLib = createModelNode(nodeTypeName, -1, -1); + collectionLib = createModelNode(nodeTypeName, -1, -1); #else auto nodeType = rootModelNode().metaInfo().isQtQuick3DNode() ? model()->qtQuick3DNodeMetaInfo() From abc0139b58c04adaf40ffd89199b5e6ba978016b Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 26 Sep 2023 17:08:20 +0300 Subject: [PATCH 095/130] QmlDesigner: Rename CollectionModel to CollectionSourceModel Change-Id: Ic6ec8809061f4fe235d71ebcdf3e9e92c6df7654 Reviewed-by: Mahmoud Badri Reviewed-by: Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/CMakeLists.txt | 2 +- ...ionmodel.cpp => collectionsourcemodel.cpp} | 42 +++++++++---------- ...lectionmodel.h => collectionsourcemodel.h} | 4 +- .../collectioneditor/collectionview.cpp | 26 ++++++------ .../collectioneditor/collectionwidget.cpp | 10 ++--- .../collectioneditor/collectionwidget.h | 6 +-- 6 files changed, 45 insertions(+), 45 deletions(-) rename src/plugins/qmldesigner/components/collectioneditor/{collectionmodel.cpp => collectionsourcemodel.cpp} (80%) rename src/plugins/qmldesigner/components/collectioneditor/{collectionmodel.h => collectionsourcemodel.h} (95%) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index dfa4f63b8b5..0556d95a7ab 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -787,7 +787,7 @@ extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/collectioneditor SOURCES - collectionmodel.cpp collectionmodel.h + collectionsourcemodel.cpp collectionsourcemodel.h collectionview.cpp collectionview.h collectionwidget.cpp collectionwidget.h singlecollectionmodel.cpp singlecollectionmodel.h diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp similarity index 80% rename from src/plugins/qmldesigner/components/collectioneditor/collectionmodel.cpp rename to src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index 18e651fc2e2..eae5d645e16 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -1,6 +1,6 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "collectionmodel.h" +#include "collectionsourcemodel.h" #include "abstractview.h" #include "variantproperty.h" @@ -8,14 +8,14 @@ #include namespace QmlDesigner { -CollectionModel::CollectionModel() {} +CollectionSourceModel::CollectionSourceModel() {} -int CollectionModel::rowCount(const QModelIndex &) const +int CollectionSourceModel::rowCount(const QModelIndex &) const { return m_collections.size(); } -QVariant CollectionModel::data(const QModelIndex &index, int role) const +QVariant CollectionSourceModel::data(const QModelIndex &index, int role) const { QTC_ASSERT(index.isValid(), return {}); @@ -33,7 +33,7 @@ QVariant CollectionModel::data(const QModelIndex &index, int role) const return {}; } -bool CollectionModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool CollectionSourceModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) return false; @@ -75,7 +75,7 @@ bool CollectionModel::setData(const QModelIndex &index, const QVariant &value, i return true; } -bool CollectionModel::removeRows(int row, int count, [[maybe_unused]] const QModelIndex &parent) +bool CollectionSourceModel::removeRows(int row, int count, [[maybe_unused]] const QModelIndex &parent) { const int rowMax = std::min(row + count, rowCount()); @@ -116,7 +116,7 @@ bool CollectionModel::removeRows(int row, int count, [[maybe_unused]] const QMod return true; } -QHash CollectionModel::roleNames() const +QHash CollectionSourceModel::roleNames() const { static QHash roles; if (roles.isEmpty()) { @@ -130,7 +130,7 @@ QHash CollectionModel::roleNames() const return roles; } -void CollectionModel::setCollections(const ModelNodes &collections) +void CollectionSourceModel::setCollections(const ModelNodes &collections) { beginResetModel(); bool wasEmpty = isEmpty(); @@ -148,7 +148,7 @@ void CollectionModel::setCollections(const ModelNodes &collections) updateSelectedCollection(true); } -void CollectionModel::removeCollection(const ModelNode &node) +void CollectionSourceModel::removeCollection(const ModelNode &node) { int nodePlace = m_collectionsIndexHash.value(node.internalId(), -1); if (nodePlace < 0) @@ -157,12 +157,12 @@ void CollectionModel::removeCollection(const ModelNode &node) removeRow(nodePlace); } -int CollectionModel::collectionIndex(const ModelNode &node) const +int CollectionSourceModel::collectionIndex(const ModelNode &node) const { return m_collectionsIndexHash.value(node.internalId(), -1); } -void CollectionModel::selectCollection(const ModelNode &node) +void CollectionSourceModel::selectCollection(const ModelNode &node) { int nodePlace = m_collectionsIndexHash.value(node.internalId(), -1); if (nodePlace < 0) @@ -171,7 +171,7 @@ void CollectionModel::selectCollection(const ModelNode &node) selectCollectionIndex(nodePlace, true); } -QmlDesigner::ModelNode CollectionModel::collectionNodeAt(int idx) +QmlDesigner::ModelNode CollectionSourceModel::collectionNodeAt(int idx) { QModelIndex data = index(idx); if (!data.isValid()) @@ -180,12 +180,12 @@ QmlDesigner::ModelNode CollectionModel::collectionNodeAt(int idx) return m_collections.at(idx); } -bool CollectionModel::isEmpty() const +bool CollectionSourceModel::isEmpty() const { return m_collections.isEmpty(); } -void CollectionModel::selectCollectionIndex(int idx, bool selectAtLeastOne) +void CollectionSourceModel::selectCollectionIndex(int idx, bool selectAtLeastOne) { int collectionCount = m_collections.size(); int prefferedIndex = -1; @@ -199,31 +199,31 @@ void CollectionModel::selectCollectionIndex(int idx, bool selectAtLeastOne) setSelectedIndex(prefferedIndex); } -void CollectionModel::deselect() +void CollectionSourceModel::deselect() { setSelectedIndex(-1); } -void CollectionModel::updateSelectedCollection(bool selectAtLeastOne) +void CollectionSourceModel::updateSelectedCollection(bool selectAtLeastOne) { int idx = m_selectedIndex; m_selectedIndex = -1; selectCollectionIndex(idx, selectAtLeastOne); } -void CollectionModel::updateNodeName(const ModelNode &node) +void CollectionSourceModel::updateNodeName(const ModelNode &node) { QModelIndex index = indexOfNode(node); emit dataChanged(index, index, {NameRole, Qt::DisplayRole}); } -void CollectionModel::updateNodeId(const ModelNode &node) +void CollectionSourceModel::updateNodeId(const ModelNode &node) { QModelIndex index = indexOfNode(node); emit dataChanged(index, index, {IdRole}); } -void CollectionModel::setSelectedIndex(int idx) +void CollectionSourceModel::setSelectedIndex(int idx) { idx = (idx > -1 && idx < m_collections.count()) ? idx : -1; @@ -243,7 +243,7 @@ void CollectionModel::setSelectedIndex(int idx) } } -void CollectionModel::updateEmpty() +void CollectionSourceModel::updateEmpty() { bool isEmptyNow = isEmpty(); if (m_isEmpty != isEmptyNow) { @@ -255,7 +255,7 @@ void CollectionModel::updateEmpty() } } -QModelIndex CollectionModel::indexOfNode(const ModelNode &node) const +QModelIndex CollectionSourceModel::indexOfNode(const ModelNode &node) const { return index(m_collectionsIndexHash.value(node.internalId(), -1)); } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h similarity index 95% rename from src/plugins/qmldesigner/components/collectioneditor/collectionmodel.h rename to src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h index 17831732543..cdde4362835 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h @@ -13,7 +13,7 @@ QT_END_NAMESPACE namespace QmlDesigner { -class CollectionModel : public QAbstractListModel +class CollectionSourceModel : public QAbstractListModel { Q_OBJECT @@ -22,7 +22,7 @@ class CollectionModel : public QAbstractListModel public: enum Roles { IdRole = Qt::UserRole + 1, NameRole, SelectedRole }; - explicit CollectionModel(); + explicit CollectionSourceModel(); virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 2f46dcbf699..2870e42db54 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "collectionview.h" -#include "collectionmodel.h" +#include "collectionsourcemodel.h" #include "collectionwidget.h" #include "designmodecontext.h" #include "nodelistproperty.h" @@ -353,11 +353,11 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.data()); Core::ICore::addContextObject(collectionEditorContext); - CollectionModel *collectionModel = m_widget->collectionModel().data(); + CollectionSourceModel *sourceModel = m_widget->sourceModel().data(); - connect(collectionModel, &CollectionModel::selectedIndexChanged, this, [&](int selectedIndex) { + connect(sourceModel, &CollectionSourceModel::selectedIndexChanged, this, [&](int selectedIndex) { m_widget->singleCollectionModel()->setCollection( - m_widget->collectionModel()->collectionNodeAt(selectedIndex)); + m_widget->sourceModel()->collectionNodeAt(selectedIndex)); }); } @@ -394,19 +394,19 @@ void CollectionView::nodeReparented(const ModelNode &node, refreshModel(); if (isCollection(node)) - m_widget->collectionModel()->selectCollection(node); + m_widget->sourceModel()->selectCollection(node); } void CollectionView::nodeAboutToBeRemoved(const ModelNode &removedNode) { // removing the collections lib node if (isCollectionLib(removedNode)) { - m_widget->collectionModel()->setCollections({}); + m_widget->sourceModel()->setCollections({}); return; } if (isCollection(removedNode)) - m_widget->collectionModel()->removeCollection(removedNode); + m_widget->sourceModel()->removeCollection(removedNode); } void CollectionView::nodeRemoved([[maybe_unused]] const ModelNode &removedNode, @@ -416,7 +416,7 @@ void CollectionView::nodeRemoved([[maybe_unused]] const ModelNode &removedNode, if (parentProperty.parentModelNode().id() != Constants::COLLECTION_LIB_ID) return; - m_widget->collectionModel()->updateSelectedCollection(true); + m_widget->sourceModel()->updateSelectedCollection(true); } void CollectionView::variantPropertiesChanged(const QList &propertyList, @@ -426,9 +426,9 @@ void CollectionView::variantPropertiesChanged(const QList &prop ModelNode node(property.parentModelNode()); if (isCollection(node)) { if (property.name() == "objectName") - m_widget->collectionModel()->updateNodeName(node); + m_widget->sourceModel()->updateNodeName(node); else if (property.name() == "id") - m_widget->collectionModel()->updateNodeId(node); + m_widget->sourceModel()->updateNodeId(node); } } } @@ -443,7 +443,7 @@ void CollectionView::selectedNodesChanged(const QList &selectedNodeLi return; if (selectedCollections.size() == 1) { // If exactly one collection is selected - m_widget->collectionModel()->selectCollection(selectedCollections.first()); + m_widget->sourceModel()->selectCollection(selectedCollections.first()); return; } @@ -455,7 +455,7 @@ void CollectionView::selectedNodesChanged(const QList &selectedNodeLi return element.parentProperty().parentModelNode() == parentElement; }); if (haveSameParent) - m_widget->collectionModel()->selectCollection(parentElement); + m_widget->sourceModel()->selectCollection(parentElement); } } @@ -499,7 +499,7 @@ void CollectionView::refreshModel() } } - m_widget->collectionModel()->setCollections(collections); + m_widget->sourceModel()->setCollections(collections); } ModelNode CollectionView::getNewCollectionNode(const Collection &collection) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 7e65516143c..4bbc1d21e42 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "collectionwidget.h" -#include "collectionmodel.h" +#include "collectionsourcemodel.h" #include "collectionview.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" @@ -36,7 +36,7 @@ namespace QmlDesigner { CollectionWidget::CollectionWidget(CollectionView *view) : QFrame() , m_view(view) - , m_model(new CollectionModel) + , m_sourceModel(new CollectionSourceModel) , m_singleCollectionModel(new SingleCollectionModel) , m_quickWidget(new StudioQuickWidget(this)) { @@ -65,7 +65,7 @@ CollectionWidget::CollectionWidget(CollectionView *view) auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend"); map->setProperties( {{"rootView", QVariant::fromValue(this)}, - {"model", QVariant::fromValue(m_model.data())}, + {"model", QVariant::fromValue(m_sourceModel.data())}, {"singleCollectionModel", QVariant::fromValue(m_singleCollectionModel.data())}}); auto hotReloadShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F4), this); @@ -82,9 +82,9 @@ void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) callback({}); } -QPointer CollectionWidget::collectionModel() const +QPointer CollectionWidget::sourceModel() const { - return m_model; + return m_sourceModel; } QPointer CollectionWidget::singleCollectionModel() const diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index e76237d0c14..55f0d3b9587 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -10,7 +10,7 @@ class StudioQuickWidget; namespace QmlDesigner { -class CollectionModel; +class CollectionSourceModel; class CollectionView; class SingleCollectionModel; @@ -22,7 +22,7 @@ public: CollectionWidget(CollectionView *view); void contextHelp(const Core::IContext::HelpCallback &callback) const; - QPointer collectionModel() const; + QPointer sourceModel() const; QPointer singleCollectionModel() const; void reloadQmlSource(); @@ -37,7 +37,7 @@ public: private: QPointer m_view; - QPointer m_model; + QPointer m_sourceModel; QPointer m_singleCollectionModel; QScopedPointer m_quickWidget; }; From 2df11c8bf782689de6601abeb7f13f2ec173b650 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 25 Sep 2023 12:11:33 +0300 Subject: [PATCH 096/130] QmlDesigner: Tweak the Lowest Common Ancestor function * LCA function accepts generic containers as the argument * LCA returns an invalid node as the result if a node belongs to a different model * An additional condition is added to check the validity of the parent nodes for the cases which a node has an invalid parent Change-Id: I9d9c1891e75a3a6a84e797deed5210c02fd92522 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Marco Bubke Reviewed-by: Mahmoud Badri --- .../designercore/model/modelutils.cpp | 18 +++++++++++++----- .../designercore/model/modelutils.h | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp index 22f472c3e5f..9ef98a39944 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -198,10 +198,11 @@ ModelNode lowestCommonAncestor(const ModelNode &node1, auto depthOfNode = [](const ModelNode &node) -> int { int depth = 0; ModelNode parentNode = node; - while (!parentNode.isRootNode()) { + while (parentNode && !parentNode.isRootNode()) { depth++; parentNode = parentNode.parentProperty().parentModelNode(); } + return depth; }; @@ -211,6 +212,11 @@ ModelNode lowestCommonAncestor(const ModelNode &node1, return node1; } + if (node1.model() != node2.model()) { + depthOfLCA = -1; + return {}; + } + if (node1.isRootNode()) { depthOfLCA = 0; return node1; @@ -250,18 +256,20 @@ ModelNode lowestCommonAncestor(const ModelNode &node1, * \brief The lowest common node containing all nodes. If one of the nodes (Node A) is * the ancestor of the other nodes, the return value is Node A and not the parent of Node A. */ -ModelNode lowestCommonAncestor(const QList &nodes) +ModelNode lowestCommonAncestor(Utils::span nodes) { - if (nodes.isEmpty()) + if (nodes.empty()) return {}; - ModelNode accumulatedNode = nodes.first(); + ModelNode accumulatedNode = nodes.front(); int accumulatedNodeDepth = -1; - for (const ModelNode &node : Utils::span(nodes).subspan(1)) { + for (const ModelNode &node : nodes.subspan(1)) { accumulatedNode = lowestCommonAncestor(accumulatedNode, node, accumulatedNodeDepth, accumulatedNodeDepth); + if (!accumulatedNode) + return {}; } return accumulatedNode; diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.h b/src/plugins/qmldesigner/designercore/model/modelutils.h index 26cf29aac00..3f8ddeab56b 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.h +++ b/src/plugins/qmldesigner/designercore/model/modelutils.h @@ -39,6 +39,6 @@ QMLDESIGNERCORE_EXPORT QList pruneChildren(const QList &no QMLDESIGNERCORE_EXPORT QList allModelNodesWithId(AbstractView *view); QMLDESIGNERCORE_EXPORT bool isThisOrAncestorLocked(const ModelNode &node); -QMLDESIGNERCORE_EXPORT ModelNode lowestCommonAncestor(const QList &nodes); +QMLDESIGNERCORE_EXPORT ModelNode lowestCommonAncestor(Utils::span nodes); } // namespace QmlDesigner::ModelUtils From db2682a5d2bfd90b465430ba2e6e895386b15ac1 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 25 Sep 2023 12:21:09 +0300 Subject: [PATCH 097/130] QmlDesigner: Add google tests for the Lowest Common Ancestor Function Change-Id: I7a0b0c2cfe8861d1d5387ebd7d79ba31ac2f1ed6 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Marco Bubke --- .../tests/unittests/model/modelutils-test.cpp | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/tests/unit/tests/unittests/model/modelutils-test.cpp b/tests/unit/tests/unittests/model/modelutils-test.cpp index 4418d36ac83..8d69e69aba8 100644 --- a/tests/unit/tests/unittests/model/modelutils-test.cpp +++ b/tests/unit/tests/unittests/model/modelutils-test.cpp @@ -3,13 +3,16 @@ #include +#include #include #include - #include #include +#include namespace { +using QmlDesigner::ModelNode; +using QmlDesigner::ModelNodes; class ModelUtils : public ::testing::Test { @@ -106,4 +109,59 @@ TEST_F(ModelUtils, component_file_path_for_non_file_component_node_is_empty) ASSERT_THAT(path, IsEmpty()); } +TEST_F(ModelUtils, find_lowest_common_ancestor) +{ + auto child1 = model.createModelNode("Item"); + auto child2 = model.createModelNode("Item"); + model.rootModelNode().defaultNodeAbstractProperty().reparentHere(child1); + model.rootModelNode().defaultNodeAbstractProperty().reparentHere(child2); + ModelNodes nodes{child1, child2}; + + auto commonAncestor = QmlDesigner::ModelUtils::lowestCommonAncestor(nodes); + + ASSERT_THAT(commonAncestor, model.rootModelNode()); +} + +TEST_F(ModelUtils, lowest_common_ancestor_return_invalid_node_if_argument_is_invalid) +{ + auto child1 = model.createModelNode("Item"); + auto child2 = ModelNode{}; + model.rootModelNode().defaultNodeAbstractProperty().reparentHere(child1); + ModelNodes nodes{child1, child2}; + + auto commonAncestor = QmlDesigner::ModelUtils::lowestCommonAncestor(nodes); + + ASSERT_THAT(commonAncestor, Not(IsValid())); +} + +TEST_F(ModelUtils, find_lowest_common_ancestor_when_one_of_the_nodes_is_parent) +{ + auto parentNode = model.createModelNode("Item"); + auto childNode = model.createModelNode("Item"); + parentNode.defaultNodeAbstractProperty().reparentHere(childNode); + model.rootModelNode().defaultNodeAbstractProperty().reparentHere(parentNode); + ModelNodes nodes{childNode, parentNode}; + + auto commonAncestor = QmlDesigner::ModelUtils::lowestCommonAncestor(nodes); + + ASSERT_THAT(commonAncestor, parentNode); +} + +TEST_F(ModelUtils, lowest_common_ancestor_for_uncle_and_nephew_should_return_the_grandFather) +{ + auto grandFatherNode = model.createModelNode("Item"); + auto fatherNode = model.createModelNode("Item"); + auto uncleNode = model.createModelNode("Item"); + auto nephewNode = model.createModelNode("Item"); + fatherNode.defaultNodeAbstractProperty().reparentHere(nephewNode); + grandFatherNode.defaultNodeAbstractProperty().reparentHere(fatherNode); + grandFatherNode.defaultNodeAbstractProperty().reparentHere(uncleNode); + model.rootModelNode().defaultNodeAbstractProperty().reparentHere(grandFatherNode); + ModelNodes nodes{uncleNode, nephewNode}; + + auto commonAncestor = QmlDesigner::ModelUtils::lowestCommonAncestor(nodes); + + ASSERT_THAT(commonAncestor, grandFatherNode); +} + } // namespace From 1bee70d07bd984d54438b8362467044d72504efa Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 19 Sep 2023 15:48:28 +0300 Subject: [PATCH 098/130] QmlDesigner: Modify the background color for the ComboBoxes on ToolBar Task-number: QDS-10706 Change-Id: Id67479d771ee0f8ac6b6116703ad2821517374cd Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- src/libs/utils/stylehelper.h | 1 + src/plugins/coreplugin/manhattanstyle.cpp | 13 +++++++++++-- .../components/componentcore/zoomaction.cpp | 1 + .../components/formeditor/backgroundaction.cpp | 1 + src/plugins/qmldesignerbase/studio/studiostyle.cpp | 5 +---- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/libs/utils/stylehelper.h b/src/libs/utils/stylehelper.h index 2b898cb1049..ffc03c1a29c 100644 --- a/src/libs/utils/stylehelper.h +++ b/src/libs/utils/stylehelper.h @@ -37,6 +37,7 @@ constexpr char C_PANEL_WIDGET[] = "panelwidget"; constexpr char C_PANEL_WIDGET_SINGLE_ROW[] = "panelwidget_singlerow"; constexpr char C_SHOW_BORDER[] = "showborder"; constexpr char C_TOP_BORDER[] = "topBorder"; +constexpr char C_TOOLBAR_ACTIONWIDGET[] = "toolbar_actionWidget"; enum ToolbarStyle { ToolbarStyleCompact, diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp index e3205dae6d8..fc4cd9459a0 100644 --- a/src/plugins/coreplugin/manhattanstyle.cpp +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -728,8 +728,17 @@ void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element, painter->drawLine(borderRect.topRight(), borderRect.bottomRight()); } } else if (option->state & State_Enabled && option->state & State_MouseOver) { - StyleHelper::drawPanelBgRect( - painter, rect, creatorTheme()->color(Theme::FancyToolButtonHoverColor)); + if (widget->property(StyleHelper::C_TOOLBAR_ACTIONWIDGET).toBool()) { + painter->save(); + painter->setBrush(creatorTheme()->color(Theme::FancyToolButtonHoverColor)); + painter->drawRoundedRect(rect, 5, 5); + painter->restore(); + } else { + StyleHelper::drawPanelBgRect(painter, + rect, + creatorTheme()->color( + Theme::FancyToolButtonHoverColor)); + } } if (option->state & State_HasFocus && (option->state & State_KeyboardFocusChange)) { QColor highlight = option->palette.highlight().color(); diff --git a/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp b/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp index e967be04ec8..a389442ec96 100644 --- a/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp +++ b/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp @@ -126,6 +126,7 @@ QWidget *ZoomAction::createWidget(QWidget *parent) if (!m_combo && parentIsToolBar(parent)) { m_combo = createZoomComboBox(parent); m_combo->setProperty(Utils::StyleHelper::C_HIDE_BORDER, true); + m_combo->setProperty(Utils::StyleHelper::C_TOOLBAR_ACTIONWIDGET, true); m_combo->setCurrentIndex(m_index); m_combo->setToolTip(m_combo->currentText()); diff --git a/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp b/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp index 07a16de7bbd..9d9139cf017 100644 --- a/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp +++ b/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp @@ -65,6 +65,7 @@ QWidget *BackgroundAction::createWidget(QWidget *parent) this, &BackgroundAction::emitBackgroundChanged); comboBox->setProperty(Utils::StyleHelper::C_HIDE_BORDER, true); + comboBox->setProperty(Utils::StyleHelper::C_TOOLBAR_ACTIONWIDGET, true); comboBox->setToolTip(tr("Set the color of the canvas.")); m_comboBox = comboBox; return comboBox; diff --git a/src/plugins/qmldesignerbase/studio/studiostyle.cpp b/src/plugins/qmldesignerbase/studio/studiostyle.cpp index 045ef43fc42..d1891ca88db 100644 --- a/src/plugins/qmldesignerbase/studio/studiostyle.cpp +++ b/src/plugins/qmldesignerbase/studio/studiostyle.cpp @@ -594,10 +594,7 @@ void StudioStyle::drawComplexControl( } } break; case CC_ComboBox: { - if (QWidget *parentWidget = widget->parentWidget()) { - QBrush bgColor = parentWidget->palette().brush(parentWidget->backgroundRole()); - painter->fillRect(option->rect, bgColor); - } + painter->fillRect(option->rect, standardPalette().brush(QPalette::ColorRole::Base)); Super::drawComplexControl(control, option, painter, widget); } break; From a914b550edbb506bd01b5b5dff95274e9c0a72db Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 27 Sep 2023 10:33:40 +0200 Subject: [PATCH 099/130] Utils: Workaround warning about missing disabled warning Change-Id: Icae1e30176fa5439a9e60be69b4eca754aca94d9 Reviewed-by: hjk Reviewed-by: Qt CI Patch Build Bot --- src/libs/utils/smallstring.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index 16e872d5afa..03c27ce30bb 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -425,10 +425,9 @@ public: size_type newSize = oldSize + string.size(); reserve(optimalCapacity(newSize)); - QT_WARNING_PUSH - QT_WARNING_DISABLE_CLANG("-Wunsafe-buffer-usage") - std::char_traits::copy(data() + oldSize, string.data(), string.size()); - QT_WARNING_POP + std::char_traits::copy(std::next(data(), static_cast(oldSize)), + string.data(), + string.size()); setSize(newSize); } From 85da9d7af5c86b0072a0852bbef5de7f7b138bb6 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 27 Sep 2023 10:46:52 +0200 Subject: [PATCH 100/130] Utils: Remove disabled warning Seems to work on my GCC but lets see if older GCC can handle it. Change-Id: I2932798da3691201f7dd4c8c981ae0dc29d01a86 Reviewed-by: hjk Reviewed-by: --- src/libs/utils/smallstring.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index 03c27ce30bb..b28e0c76f6f 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -33,11 +33,6 @@ #define unittest_public private #endif -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#endif - namespace Utils { template @@ -926,7 +921,3 @@ SmallString operator+(const char(&first)[Size], SmallStringView second) } } // namespace Utils - -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif From 38500a5c689c373d3b0da5074c726d5779b171c6 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Tue, 26 Sep 2023 18:07:21 +0200 Subject: [PATCH 101/130] QmlDesigner: Fix unenclosed multistrings Task-number: QDS-10681 Change-Id: Ide92c93c85f0d61b48c4c1cb0096cd8db201238d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Aleksei German --- .../components/bindingeditor/actioneditor.cpp | 40 ++++++++++++++++++- .../components/bindingeditor/actioneditor.h | 2 + 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp index 0260077da86..8480115a3b7 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp @@ -100,7 +100,37 @@ QString ActionEditor::connectionValue() const if (!m_dialog) return {}; - return m_dialog->editorValue(); + QString value = m_dialog->editorValue().trimmed(); + + //using parsed qml for unenclosed multistring (QDS-10681) + const QString testingString = QString("Item { \n" + " onWidthChanged: %1 \n" + "}") + .arg(value); + + QmlJS::Document::MutablePtr firstAttemptDoc = QmlJS::Document::create({}, + QmlJS::Dialect::QmlQtQuick2); + firstAttemptDoc->setSource(testingString); + firstAttemptDoc->parseQml(); + + if (!firstAttemptDoc->isParsedCorrectly()) { + const QString testingString2 = QString("Item { \n" + " onWidthChanged: { \n" + " %1 \n" + " } \n" + "} \n") + .arg(value); + + QmlJS::Document::MutablePtr secondAttemptDoc = QmlJS::Document::create({}, + QmlJS::Dialect::QmlQtQuick2); + secondAttemptDoc->setSource(testingString2); + secondAttemptDoc->parseQml(); + + if (secondAttemptDoc->isParsedCorrectly()) + return QString("{\n%1\n}").arg(value); + } + + return value; } void ActionEditor::setConnectionValue(const QString &text) @@ -109,6 +139,14 @@ void ActionEditor::setConnectionValue(const QString &text) m_dialog->setEditorValue(text); } +QString ActionEditor::rawConnectionValue() const +{ + if (!m_dialog) + return {}; + + return m_dialog->editorValue(); +} + bool ActionEditor::hasModelIndex() const { return m_index.isValid(); diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h index 29e5b1d99a6..9a14ad2117c 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h @@ -37,6 +37,8 @@ public: QString connectionValue() const; void setConnectionValue(const QString &text); + QString rawConnectionValue() const; + bool hasModelIndex() const; void resetModelIndex(); QModelIndex modelIndex() const; From 4928c2a108c35f34b44864bc65381f56180afe94 Mon Sep 17 00:00:00 2001 From: Johanna Vanhatapio Date: Thu, 28 Sep 2023 11:46:34 +0300 Subject: [PATCH 102/130] Doc: Describe configuring snapping Task-number: QDS-10752 Change-Id: Idd622e34b034db0ba38411023b5cd6cf46c174f6 Reviewed-by: Mats Honkamaa Reviewed-by: Miikka Heikkinen --- .../qtdesignstudio-3d-editor.qdoc | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc index 454a7a0845d..39b0b05b048 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc @@ -239,15 +239,22 @@ \section2 Configuring Snapping - To edit snapping settings, select \inlineimage icons/snapping-3d-conf.png - in the \uicontrol 3D view toolbar. + To edit the snapping settings, select \inlineimage icons/snapping-3d-conf.png + in the \uicontrol 3D view toolbar to open the configure dialog. In the configure dialog, you can do the following: \list - \li Turn on and off snapping separately for the different transformations + \li Turn snapping on and off separately for the different transformations (move, rotate, scale). - \li Set snap intervals. - \li Toggle if the position snaps to absolute or relative values. + \li Set snap intervals for the transformations. + \note Changing the snap interval for the position also changes the grid line intervals. + \note All the grid lines might not be visible depending on the zoom level in the 3D view. + \li Select \uicontrol {Absolute Position} to snap to absolute values. Clear the checkbox + to use relative values.The absolute snapping aligns the object with the grid, while the + relative snapping moves the object in fixed intervals without changing its alignment. + For example, if you have an object that is slightly off the grid and you want to snap it + to the grid, use the absolute snapping. If you want to move the object by a certain + distance without affecting its orientation, use the relative snapping. \endlist \section1 Aligning Views and Cameras From 1aeade09c9425b77afd1aa41f4c5e8dbe6723751 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Fri, 29 Sep 2023 11:53:01 +0200 Subject: [PATCH 103/130] QmlDesigner: Fix MCUs project wizard Task-number: QDS-10802 Change-Id: I94591458ced09ea483df75f8e5bfdd4b506baca0 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../studio_templates/projects/application-mcu/wizard.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json index 5c27a294467..01baa5f9760 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json @@ -188,7 +188,7 @@ { "isBinary": true, "source": "MCUDefaultStyle/images", - "target": "%{ProjectDirectory}/MCUDefaultStyle/images" + "target": "%{ProjectDirectory}/imports/MCUDefaultStyle/images" } ] } From fc03a77ee1a754e15dbf62354c83ae87f0399f8c Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Mon, 25 Sep 2023 17:26:46 +0200 Subject: [PATCH 104/130] QmlDesigner: Update Screenshot of Connection overview document This patch updates the screenshot for Connection view documentation. Changes the old "png" image to new "webp". It shows new design and elements of the Connection view. Fixes: QDS-10760 Change-Id: I2199afae5f044dc70ad4a6f51b59fd9c7f9c7f3d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mats Honkamaa --- .../images/qmldesigner-connections.png | Bin 3294 -> 0 bytes .../images/qmldesigner-connections.webp | Bin 0 -> 7158 bytes .../src/views/qtquick-connection-view.qdoc | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 doc/qtdesignstudio/images/qmldesigner-connections.png create mode 100644 doc/qtdesignstudio/images/qmldesigner-connections.webp diff --git a/doc/qtdesignstudio/images/qmldesigner-connections.png b/doc/qtdesignstudio/images/qmldesigner-connections.png deleted file mode 100644 index 605dc8357845c869198de3567b747ae662133d1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3294 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 L>gTe~DWM4f2BAWn diff --git a/doc/qtdesignstudio/images/qmldesigner-connections.webp b/doc/qtdesignstudio/images/qmldesigner-connections.webp new file mode 100644 index 0000000000000000000000000000000000000000..5a49a316fe6da020110735144a72d53641ae586f GIT binary patch literal 7158 zcmWIYbaQ(r&A<@u>J$(bVBzyfnt?&TzEPgRwEfYxT;-F$*WcCl{4Z$8sIg)}gX+z# z3e(cHr+A#XbzI`=n|(pbYU?V+K5h@(e0cXanZR{QL1)<7ix$7FzwP$<$(-W87fZF; zgO+n9iAS$lkg&VFU-RGN2mjM;9=}^|!=iA$^;{+~wtE zre$-zbPv2-EhBDlT!4ikV6x1b7kn!lgIRbEIi&5`_hN(S5;M2w7g#s=F3P=~6(y^* zgJt=S>b(IPd)C*BZV4&e{z#yyZNiuPjki6zp1gW@H~Cq0#B6{@%443EcjE z?)Q<$62HG$r_}{c;yI$goU5^k)$H))Bu@uru8SF30*z@xskhBkPM(<=uP?2Cx-4y4 z?(J>4>@187r`=}d-rlx%>E^SW|AyV(cJ|-u!rNv^@BL!u{!?K8FrDFProhgI*0Q4- zj<&xJT<90Oalq+;is7E}cKM@YIiQ-m)q#`|Rhl1mwF5?pqMg@t@D}=#|F`CxZ<(+DMtZ6nNGz3wX1A z(ch`h=J=hQReI-x`?a|{RsLPM@Wo<&>As@sTMDO7A5~f2Ep)o$y{@t9r^B6ln&xkm z{SnT2yZxHNN%w2E3d;n~U#|EUY`iz}c~jUSQ$LeR`^%3Hb+M}$oowkg{8YVjV|n4O zDbt!4E_t?)Yp(-KWRlnC`>wm<&p+;XUYOP%)mIZ$I8WeD@GahBJDbl4r3RIBp0CeR z{O~qv&Z-NCEG{Z3PEP9Y`QUh&J8EIFfwICHH|yoq?=GI;UD0x2N8yVdaTZe@3^JD2 z<@`Rw@k(~hJ?j^9=U8V{l}rd&5HmGB?al>O>vVR@bT-Zdi@HTm6mF9|^JSfX=z{&_ z`;(MkrN4jp|88bJ(}nA14LV=8?w`?Ey(uJT+o2B@7Q(jCRq5do7k~6C9R6D+~7+yPB8`O4i_cOcizxCTV6rWge>i6v~Q&-t| zEmina`EHKAC7bjf%@oG)BJib3kZ39UTl z?1d>er$sR;crKgzuhEX-#lfWqPVKsVZJMuVX#SNi{TkZ;RjUXZeU<-X*j5#ev-IqZ8shTD4_EiKWGyRt@Ey0p>WzkL0S z?uPQ+HSfMoS;G{~z;boz>SGaXFC3&9o-9gt|8rK{b<-J<4vSCe*1zYj3e&G!v?6jx z(yVi{uP5aBHh$(f+CQ~{ZCi&D=gxZfJ7;S5@2I&h=f|x4!qDZ=_qpASPn|B$V}H8a zr-nVDH!fFttMTqPOa+Xs%%|PV5BJ=^KUHMe5B1wh+jiPAEXdG}nygn}Hu3b6Ae+3~ z$F6C~pICe6s>@~vZmDIxnw#3ww_fB8JySXJ&b!)thuIDpx1$%U)w}H9wJK3ho$uV0 z46bL!Jonw!?ln5A`zi0(^9>^Jo?ocVclf2bLT;*@(Z<@7m0uqo-Rr+baev^AixYls zj#{9@Hpyy$ zq?s&sKRW688JRxTdZnU(*bk>udihVNoO@Ym6Uw>h_y1$XK@Mi6u4gwq*|9@}f7ds4 z?Jv8|Om9C{AoZegpL0Qr^NE_|`Kue02G z+~2ir_52C_FWI-wSLSvMzG&!^H`D9;mfeZJxEX%kYCB?oXUV%Wy}2sqjtTQVQT}kr z{L+0_J?`6{Zg%T#EqfI6`Nf9sANU#SB%VsdJZgTSrg7;SZ?V*rfGMlCUO8hfH*a0W zC-v1F45jtgW_gL(?7HXnHU8DHYwBNf`ZR9aJL@i_%N-)5QpSTXZT!DnaF z1_p+v8{3k5T&;T_|2-mdahsRpZ3{IerN=i?>%DlM26U^wmtI&sZQ|Ys%sWoh_G^{z z+!1##s_cv(gXjmw4@-Q_zOW_4$VqHWzG}CR+tm0#DVzMaFUNlQg>Jh4`S-haqf@gM z7zF=*e|lTi{AoY5KZ(D8w@}&XX-s4A|Ci@Bg@j#kdVRchS|Xptv=d+ZTy1UjzxG>r zoPG9U!rTR4RNn^NOq;SZ^8ChQyL>q|R(^l`yF8C|Qv4+?=4tB3)_(iG@W;MKjk85G z)~-=vFP0yOFtn|bLRINPxscIQ<)&|-U7bIAOjtL~wkbSXV`tWO>HcK_ zhp>rnZ*Bi_u=dz$$>^4Cy}QbGoY~Y0{>{3RHgj)oGB=tfIFnuG zxnoZ_4X0VksqGYxxiuvx>G-Edq1u6WmODOL_UZ4B2^{adYxhrk^MHpT zRE5D-f3pzB6+Z{28CQ*zkHnbsLgfpX6=GCtMB%{6mC9v zVBZVhcb9khui=*2#;H5!tL%R@miRAe+E*>Ui>_Z1q1s{NEiK#kcI7%L9=pD~Gi49o z6Hq;=Yrk&N+QU5B=e{{i?0M`uF@9O2z@qmI2Oiw5o8$5+#J-tff}H*?BYnI3i#(Uj z+qP<=onx8blBA%}1#adhPY=0<-<&;PByI1XGuGK6k+Z^c`ZQS@(hSnWlr=e+PI4{E zwe2#h?Rax+|1oyk{2h~>&0c@Fx&G(W!|{cZQMa7eG;mGGo0GoK_{uf&n>Vi>t9v@< z=fT74wLI?T_!%cz_U5)`%e;?xBWqq9w}0c*yX?xl+%M*T6ukbrTackeQ?^Dpn)ZBfzR&a-}LQQtQGmnU~^GX5F+4M>Jo_9_E!>cDyb3;2&SAik?N8WZ1TmQsmVl49)^((hp z?yA50_{_&Y2N&JxTe-hN%Ytg-Niw6%8dOn;mvUTK>AIf9G7;Ct%yBwuyS1*|L#ae;gOZGQXzY|!r9`4Nf?7ZyFL4ON=_ zMbLivtuss&UoIN@9)09-W3s9H2;V)(1 zA0oZjJvu+`RrscF%c5&rLoJNDCMG5@I{t3!-!8D1zlYO@>wm`OFM>zbotYMxKVeeL zqF!V5SzfQxdIi1O1PaA#fBZ}r;Jo-GTqPaey@ACexv zIK8e#dTnlY%LV^u8@@eL%`P*UC$@awe|k=#H@FFad6!l+sQf~>K<|L z(owB&<6v~ia1Onx%Bs__MsaII@wK1gl_Eda?$0}7ve>-DzFuO-10TmL^@sa5xd#~V zUz=Vwe{Zn!;(Kb#*J*ebyjPKQ(n#O1ZbO;r!gp-~KJS>XADqwdK;*GcXUL!5JbbEc z0nT|Jw=>OUOW?73^XmJdyH(Ypi?$y$ILeVBb-~|G?5)}I^w{I`*N3m=&#&uPd9Gr9 z=!-pjzxMS9OR02Bh*VTgWq+2#_4mZy7v&wkVFgcn=BpYSjXB+DTk60fQUTv)M{}c0q6=EVc4PrKwuu3~hy|Gvt_vFL=jK?PY z^VchfKJ=T=^&*v}sBxFj@296q&;ChVxFS!_^QpY^u4y@&{(WCp_4)Ds@QHd7dukUh z%TvGQC=ukmTa5R9*pX|~_6C;Si2K}-{xme;o#>7fapjkbY8NgNd$*lw>w)<*i$CNu z2x|SF@nUDC`kDT!2=~_vO=;H`dHGu3+RS}d;Ly{Kx+CjXcqh-@Jz=fiwI@tQrhm8Q z?KGZwT_Q+^k%fELao09UNv0$iBXn+VbqI@+qzj2c7*{9^O@F7t`H*XcbSx zZnN%Jq1!$z)a!l|wza~hu&mcy|Ajl3chRajk%{(lnHGh)Q?lFVKkc8ld6UPFX|r_B z>^YUPpHPt;@TZ~`3q`OY$QSH0Up}VQMFyC|a+0;Wl4|~|BN}mhnYT2Au zQf6{|am1@l8t3J7Z5~Ilr>3wny!^Q6vBw|o1sfQ{zx-uYQi*k(eWZEYv{M0G3i3CL zV~rUY89a1S`@WPnMosW|>HI;DsbRi@B;VuMBLZ7gZLfNM)zfiE1H& z^_{av#VKsgx+Uv{PCie1edC@h!Y9qV>1n6xD0Dt9im%CT`i; z>^#eK$@+Sh1e*={x#=6zdcJV=dhp#gV&n$>ii--be{$>AlLAE7nGuefE0%$xh{Tva9O;ckS8| zt33)Sm4<8BKFJW)Az=oyFP#F1MAlP?|(fIEMlyc{dN9e z(hVOS_oJd$-Cr7>wm##;#9%ywi$}Fqt$FiDJ5Rw~8`pHs3~OeLV@S1p^ESyK=TNzl zh0@uEe1C@NJt^}z8~keyIh=iUCPTec_QzILj2_Pj1@-w?O@jSu&W8~t-$m*?uMTk{$#Dy-L& zHLAam@m%Ej`8T)w>I`m4R(;;K?Xdr**9^QVg2mMa4!N%tT6Pu59O-b3jD5TFpE28q zBB3KMTb9gNHt)!t-C8XI?^tZ!h|dl7l;8Hit?GKLME&7)nL?VAvb!*TV316 zqROnzn=JSFCi|<*S9D@mzOgzXEn&m6swmrik7wyJ-+sV!i+`_g{b7bPm$nOZ@G$>w z)@-~GnQXeY)^m2|iw*Y8(~RGD{xk_#{(2^tb*b|54=io_)SmZBP8Q%*Y+>7!EBheU z_sTs@qn3v2qGfu=TwWOX$aH>WTfT66<jYd!wq(!YJrmovXm zx|XEE;avy{%+tNqYm z3uk{`c9hXck@p2M-Z`S#53=L>&h~p`?A>7UCFHTt^^~{KkG)@9`7ZnXx6KW!4G+p^ zRGIxQ{;MJ{)B0@N>dD=2S;NGgr++!nS~ZCy^6_EelB7qQ_S@)cd=uf>^gh(%$BB!E z^Y%pjDeUVClDRuaujNVn4uxwe2Q%WdSG06}nLPEY$~KdPS$9O!Cog!WY|>&s`J2U| z1?%=s3NQ5Ba?WYu;ddq~e~fLO)|ql}WX;cKxxO-6%I9dq zo?X8dojhh0cjdZ!myfJ|(wcknuWI({{7gIeGgUs~&R27`&yTV`M%MmpcrS6ZW!erd z+kHtFFLEDt4JkPhv}JDT5@|$mOAYb#1PgC+rrqAU))Mj4K|FOx^QGc!3?QJhPk2HKa60Gpq z?6s5qO2f_zucTw%TN&(|^F|>~&w0|K*Y!qj_iFD{FN{u((OZygzt80VA^-9g!9d?H zKj+PFl?dpRo6A}LGyUB4-`lPWXV`YG`=Yf)g+u=D%W_E{)>}VnRhIXzZ)@C~u-apPv4s)#dQb?v=N+wJ-n9kIs_bv0e1U4EHG|GiTjcGDRr&q4*h= z2}@#nY%-nS?cZNg7h&Q*@x#I;zhz{x(P9LG!6q1!2d1nJco~ z5)YlXKIHEICw87e^-sphG7cQ3?vGD?wogpbtW^4TMnHbrid{b~SF)NGYMi{sxh_b8 z<-=*+{(n<$znT8z^r7MlWg?UP_a8VJr$0A;`x^b_6PCRAJ>SQ${Qde+y?q}h%i4UI z9pWXKyI8tj(bD3_!H4R(7yf?VdxYt5_Q}NWs~g)}Ra`HB&gk2vZL_#-Uew)Bvtwef z%T7+tZMyf*H!|3b(arSLqOaThudfWacH*dn*R`E%j=bN;qIjfX!>^yqUKo9~`LaTN zT6ttr!6_cI5BL7}TFl>Q+tB$m<@~?RYzz3bR{#FHJ$LQn_8Ghj7N*QPq4noo_ph9p zY1X;2^2SkDzn%Ir{|ZN{)-F}o%G50n)w6fGYjK7cbY5til>0AoTB#M+g)<(rx;L^r zA1rKdKeO_He(h9K8MD2Cd-lDZRq({u*?p1Xe{1notDZG4<8KQswJBc4|0iKNBlDz3 zm)_3Gn)N=q#VLbt_1dDp5)wICXIPHsc&e*N-K$brub*~&SN#7eokbU8H*J%7$|D=& z^)yp5)AfhOf8C2lvmb1fT`syicbf-ye}#0hy!O-+@kbJ^>mqXB{&25PoKsq&_x`JG z_JJ>d-dtMjberpY&S4AZuH8KcSFLkTcU>d6D9XU$$<6H-rZXOGU-50j+uje-x6cWd zpHp#oZTNa+>;e5h3bn7fbT|LGxL2fwFF5+JOUU~9zY;G+FWorvJ;P1r@13HPBv#cm zSQeR`-M~{XYRvG}KmJ1Syh$^4!q1fFvG3VR0dB3I{ zd1CQvM=`%-@#=)VM#nup;%~9MxuvwDMdH96w`H@IZgEcZ>h%_XeD!0@zL|d7?`hz6xF(lJkR@o_H~Tf5%C^kTJgwj5 zl4Q>6#3f1Z@2CRav)9{{+ALJ(8^ziN-xjpu$*sI? z_~HG+yC>8H)GYQ@C;qHn?)=}eb|VzZ;P{Pv>Mdef%aEq8r8mQE2o zxZ+Omzx(Th(-?SFe5`{L7!80NNZe{MBdzV-T~Vu{d4Z$3OZYp19;E!d6a z@!zRayi*$2zhly_j$1QTJpbApt1jM-rSkE{kCJA;;ZpO_AzBRPJpS~gI_{HMAbpi(q*Eq`_ zyz>3EfYi#0vbUPo%+i=nEPHEt;=qa1I|9r0ZTqWZ<8X76iR`4whZ*d*q~_n1vsHdu M{qMf@UJrf&0Di;$0ssI2 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-view.qdoc index 93fbdc7ea7a..5cc2c4a6670 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-view.qdoc @@ -12,7 +12,7 @@ to create connections between components and the application, to bind component properties together, and to add custom properties for components. - \image qmldesigner-connections.png "The Connections view" + \image qmldesigner-connections.webp "The Connections view" The \l{glossary-component}{components} of the application UI and the application logic need to communicate with each other. For example, a From 42f231e4a2366b161dfd08a6b480d5ac30a40246 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 29 Sep 2023 15:44:56 +0300 Subject: [PATCH 105/130] QmlDesigner: Fix 3D view grid color when there are objects behind it Fixes: QDS-10803 Change-Id: Ibbe1c9d308031b941df42750eb4f75cb2fe43c13 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml | 4 ++++ .../qml2puppet/mockfiles/shaders/gridmaterial.frag | 12 +++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml b/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml index e9d94b54c18..e189d064014 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml @@ -71,6 +71,7 @@ Node { orthoMode: grid.orthoMode } ] + opacity: 0.99 } Model { // Subdivision lines @@ -90,6 +91,7 @@ Node { orthoMode: grid.orthoMode } ] + opacity: 0.99 } Model { // Z Axis @@ -108,6 +110,7 @@ Node { orthoMode: grid.orthoMode } ] + opacity: 0.99 } Model { // X Axis readonly property bool _edit3dLocked: true // Make this non-pickable @@ -126,5 +129,6 @@ Node { orthoMode: grid.orthoMode } ] + opacity: 0.99 } } diff --git a/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag b/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag index a1ec3bffff2..7d785bc5312 100644 --- a/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag +++ b/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag @@ -19,9 +19,15 @@ void MAIN() if (depth > 90000.0) alpha *= clamp((100000.0 - depth) / 10000.0, 0, 1); - if (alpha > 0.01) - FRAGCOLOR = vec4(color.x * alpha, color.y * alpha, color.z * alpha, alpha); - else + if (alpha > 0.01) { + vec2 uv = FRAGCOORD.xy / vec2(textureSize(SCREEN_TEXTURE, 0)); + vec4 sc = texture(SCREEN_TEXTURE, uv); + if (sc.a == 0.0) + FRAGCOLOR = vec4(color.xyz * alpha, alpha); + else + FRAGCOLOR = vec4((color.xyz * alpha + sc.xyz * (1.0 - alpha)) * alpha, alpha); + } else { discard; + } } } From 01a4f087c6675e49306b21ea7f205e5a0f01837d Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 22 Sep 2023 10:50:06 +0300 Subject: [PATCH 106/130] QmlDesigner: Use Studio.Models as the source of the CollectionEditor QtQuick.Studio.Models JSON and CSV components are used as the source of the Collection Editor. Collections are placed underneath the sources in the collections view Task-number: QDS-10809 Task-number: QDS-10462 Change-Id: Ia0c9cb587c462fcba98934b15068582f3f9c19c5 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../CollectionItem.qml | 5 +- .../CollectionView.qml | 8 +- .../ModelSourceItem.qml | 331 +++++++++++ src/plugins/qmldesigner/CMakeLists.txt | 2 + .../collectioneditorconstants.h | 14 + .../collectioneditor/collectionlistmodel.cpp | 147 +++++ .../collectioneditor/collectionlistmodel.h | 50 ++ .../collectionsourcemodel.cpp | 251 ++++++-- .../collectioneditor/collectionsourcemodel.h | 43 +- .../collectioneditor/collectionview.cpp | 544 +++--------------- .../collectioneditor/collectionview.h | 15 +- .../collectioneditor/collectionwidget.cpp | 30 +- .../collectioneditor/collectionwidget.h | 1 + .../singlecollectionmodel.cpp | 141 ++++- .../collectioneditor/singlecollectionmodel.h | 25 +- .../qmldesigner/qmldesignerconstants.h | 1 - 16 files changed, 981 insertions(+), 627 deletions(-) create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml index 5be2dacb8d7..a92cdf065d8 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml @@ -84,8 +84,9 @@ Item { Text { id: threeDots - text: "..." - font.pixelSize: StudioTheme.Values.baseFontSize + text: StudioTheme.Constants.more_medium + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: StudioTheme.Values.baseIconFontSize color: textColor anchors.right: boundingRect.right anchors.verticalCenter: parent.verticalCenter diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index 30128ecadf4..e6fddf33ee4 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -82,14 +82,14 @@ Item { spacing: 2 HelperWidgets.IconButton { - icon: StudioTheme.Constants.translationImport + icon: StudioTheme.Constants.downloadjson_large tooltip: qsTr("Import Json") onClicked: jsonImporter.open() } HelperWidgets.IconButton { - icon: StudioTheme.Constants.translationImport + icon: StudioTheme.Constants.downloadcsv_large tooltip: qsTr("Import CSV") onClicked: csvImporter.open() @@ -112,13 +112,13 @@ Item { } ListView { - id: collectionListView + id: sourceListView width: parent.width height: contentHeight model: root.model - delegate: CollectionItem { + delegate: ModelSourceItem { onDeleteItem: root.model.removeRow(index) } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml new file mode 100644 index 00000000000..9496ee394a7 --- /dev/null +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml @@ -0,0 +1,331 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import HelperWidgets 2.0 as HelperWidgets +import StudioControls 1.0 as StudioControls +import StudioTheme as StudioTheme + +Item { + id: root + + implicitWidth: 300 + implicitHeight: wholeColumn.height + 6 + + property color textColor + property var collectionModel + + property bool expanded: false + + signal selectItem(int itemIndex) + signal deleteItem() + + Column { + id: wholeColumn + + Item { + id: boundingRect + + anchors.centerIn: root + width: root.width - 24 + height: nameHolder.height + clip: true + + MouseArea { + id: itemMouse + + anchors.fill: parent + acceptedButtons: Qt.LeftButton + propagateComposedEvents: true + hoverEnabled: true + + onClicked: (event) => { + if (!sourceIsSelected) { + sourceIsSelected = true + event.accepted = true + } + } + + onDoubleClicked: (event) => { + if (collectionListView.count > 0) + root.expanded = !root.expanded; + } + } + + Rectangle { + id: innerRect + anchors.fill: parent + } + + Row { + width: parent.width - threeDots.width + leftPadding: 20 + + Text { + id: expandButton + + property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle + + width: expandButton.style.squareControlSize.width + height: nameHolder.height + + text: StudioTheme.Constants.startNode + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: expandButton.style.baseIconFontSize + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: textColor + + rotation: root.expanded ? 90 : 0 + + Behavior on rotation { + SpringAnimation { spring: 2; damping: 0.2 } + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton + Qt.LeftButton + onClicked: (event) => { + root.expanded = !root.expanded + event.accepted = true + } + } + visible: collectionListView.count > 0 + } + + Text { + id: nameHolder + + text: sourceName + font.pixelSize: StudioTheme.Values.baseFontSize + color: textColor + leftPadding: 5 + topPadding: 8 + rightPadding: 8 + bottomPadding: 8 + elide: Text.ElideMiddle + verticalAlignment: Text.AlignVCenter + } + } + + Text { + id: threeDots + + text: StudioTheme.Constants.more_medium + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: StudioTheme.Values.baseIconFontSize + color: textColor + anchors.right: boundingRect.right + anchors.verticalCenter: parent.verticalCenter + rightPadding: 12 + topPadding: nameHolder.topPadding + bottomPadding: nameHolder.bottomPadding + verticalAlignment: Text.AlignVCenter + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton + Qt.LeftButton + onClicked: (event) => { + collectionMenu.popup() + event.accepted = true + } + } + } + } + + ListView { + id: collectionListView + + width: parent.width + height: root.expanded ? contentHeight : 0 + model: collections + clip: true + + Behavior on height { + NumberAnimation {duration: 500} + } + + delegate: CollectionItem { + width: parent.width + onDeleteItem: root.model.removeRow(index) + } + } + } + + StudioControls.Menu { + id: collectionMenu + + StudioControls.MenuItem { + text: qsTr("Delete") + shortcut: StandardKey.Delete + onTriggered: deleteDialog.open() + } + + StudioControls.MenuItem { + text: qsTr("Rename") + shortcut: StandardKey.Replace + onTriggered: renameDialog.open() + } + } + + StudioControls.Dialog { + id: deleteDialog + + title: qsTr("Deleting source") + + contentItem: Column { + spacing: 2 + + Text { + text: qsTr("Are you sure that you want to delete source \"" + sourceName + "\"?") + color: StudioTheme.Values.themeTextColor + } + + Item { // spacer + width: 1 + height: 20 + } + + Row { + anchors.right: parent.right + spacing: 10 + + HelperWidgets.Button { + id: btnDelete + + text: qsTr("Delete") + onClicked: root.deleteItem(index) + } + + HelperWidgets.Button { + text: qsTr("Cancel") + onClicked: deleteDialog.reject() + } + } + } + } + + StudioControls.Dialog { + id: renameDialog + + title: qsTr("Rename source") + + onAccepted: { + if (newNameField.text !== "") + sourceName = newNameField.text + } + + onOpened: { + newNameField.text = sourceName + } + + contentItem: Column { + spacing: 2 + + Text { + text: qsTr("Previous name: " + sourceName) + color: StudioTheme.Values.themeTextColor + } + + Row { + spacing: 10 + Text { + text: qsTr("New name:") + color: StudioTheme.Values.themeTextColor + } + + StudioControls.TextField { + id: newNameField + + actionIndicator.visible: false + translationIndicator.visible: false + validator: newNameValidator + + Keys.onEnterPressed: renameDialog.accept() + Keys.onReturnPressed: renameDialog.accept() + Keys.onEscapePressed: renameDialog.reject() + + onTextChanged: { + btnRename.enabled = newNameField.text !== "" + } + } + } + + Item { // spacer + width: 1 + height: 20 + } + + Row { + anchors.right: parent.right + spacing: 10 + + HelperWidgets.Button { + id: btnRename + + text: qsTr("Rename") + onClicked: renameDialog.accept() + } + + HelperWidgets.Button { + text: qsTr("Cancel") + onClicked: renameDialog.reject() + } + } + } + } + + HelperWidgets.RegExpValidator { + id: newNameValidator + regExp: /^\w+$/ + } + + states: [ + State { + name: "default" + when: !sourceIsSelected && !itemMouse.containsMouse + + PropertyChanges { + target: innerRect + opacity: 0.4 + color: StudioTheme.Values.themeControlBackground + } + + PropertyChanges { + target: root + textColor: StudioTheme.Values.themeTextColor + } + }, + State { + name: "hovered" + when: !sourceIsSelected && itemMouse.containsMouse + + PropertyChanges { + target: innerRect + opacity: 0.5 + color: StudioTheme.Values.themeControlBackgroundHover + } + + PropertyChanges { + target: root + textColor: StudioTheme.Values.themeTextColor + } + }, + State { + name: "selected" + when: sourceIsSelected + + PropertyChanges { + target: innerRect + opacity: 0.6 + color: StudioTheme.Values.themeControlBackgroundInteraction + } + + PropertyChanges { + target: root + textColor: StudioTheme.Values.themeIconColorSelected + } + } + ] +} diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 0556d95a7ab..78810346650 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -787,6 +787,8 @@ extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/collectioneditor SOURCES + collectioneditorconstants.h + collectionlistmodel.cpp collectionlistmodel.h collectionsourcemodel.cpp collectionsourcemodel.h collectionview.cpp collectionview.h collectionwidget.cpp collectionwidget.h diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h new file mode 100644 index 00000000000..d75fe221e91 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h @@ -0,0 +1,14 @@ +// 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 + +namespace QmlDesigner::CollectionEditor { + +inline constexpr char SOURCEFILE_PROPERTY[] = "sourceFile"; + +inline constexpr char COLLECTIONMODEL_IMPORT[] = "QtQuick.Studio.Models"; +inline constexpr char JSONCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Models.JsonSourceModel"; +inline constexpr char CSVCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Models.CsvSourceModel"; + +} // namespace QmlDesigner::CollectionEditor diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp new file mode 100644 index 00000000000..4c49d5e4954 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp @@ -0,0 +1,147 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "collectionlistmodel.h" + +#include "collectioneditorconstants.h" +#include "variantproperty.h" + +#include +#include + +namespace { + +template +bool containsItem(const std::initializer_list &container, const ValueType &value) +{ + auto begin = std::cbegin(container); + auto end = std::cend(container); + + auto it = std::find(begin, end, value); + return it != end; +} +} // namespace + +namespace QmlDesigner { + +CollectionListModel::CollectionListModel(const ModelNode &sourceModel) + : QStringListModel() + , m_sourceNode(sourceModel) +{ + connect(this, &CollectionListModel::modelReset, this, &CollectionListModel::updateEmpty); + connect(this, &CollectionListModel::rowsRemoved, this, &CollectionListModel::updateEmpty); + connect(this, &CollectionListModel::rowsInserted, this, &CollectionListModel::updateEmpty); +} + +QHash CollectionListModel::roleNames() const +{ + static QHash roles; + if (roles.isEmpty()) { + roles.insert(Super::roleNames()); + roles.insert({ + {IdRole, "collectionId"}, + {NameRole, "collectionName"}, + {SelectedRole, "collectionIsSelected"}, + }); + } + return roles; +} + +bool CollectionListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid()) + return false; + + if (containsItem({IdRole, Qt::EditRole, Qt::DisplayRole}, role)) { + return Super::setData(index, value); + } else if (role == SelectedRole) { + if (value.toBool() != index.data(SelectedRole).toBool()) { + setSelectedIndex(value.toBool() ? index.row() : -1); + return true; + } + } + return false; +} + +QVariant CollectionListModel::data(const QModelIndex &index, int role) const +{ + QTC_ASSERT(index.isValid(), return {}); + + switch (role) { + case IdRole: + return index.row(); + case NameRole: + return Super::data(index); + case SelectedRole: + return index.row() == m_selectedIndex; + } + + return Super::data(index, role); +} + +int CollectionListModel::selectedIndex() const +{ + return m_selectedIndex; +} + +ModelNode CollectionListModel::sourceNode() const +{ + return m_sourceNode; +} + +QString CollectionListModel::sourceAddress() const +{ + return m_sourceNode.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value().toString(); +} + +void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne) +{ + int collectionCount = stringList().size(); + int preferredIndex = -1; + if (collectionCount) { + if (selectAtLeastOne) + preferredIndex = std::max(0, std::min(idx, collectionCount - 1)); + else if (idx > -1 && idx < collectionCount) + preferredIndex = idx; + } + + setSelectedIndex(preferredIndex); +} + +QString CollectionListModel::collectionNameAt(int idx) const +{ + return index(idx).data(NameRole).toString(); +} + +void CollectionListModel::setSelectedIndex(int idx) +{ + idx = (idx > -1 && idx < rowCount()) ? idx : -1; + + if (m_selectedIndex != idx) { + QModelIndex previousIndex = index(m_selectedIndex); + QModelIndex newIndex = index(idx); + + m_selectedIndex = idx; + + if (previousIndex.isValid()) + emit dataChanged(previousIndex, previousIndex, {SelectedRole}); + + if (newIndex.isValid()) + emit dataChanged(newIndex, newIndex, {SelectedRole}); + + emit selectedIndexChanged(idx); + } +} + +void CollectionListModel::updateEmpty() +{ + bool isEmptyNow = stringList().isEmpty(); + if (m_isEmpty != isEmptyNow) { + m_isEmpty = isEmptyNow; + emit isEmptyChanged(m_isEmpty); + + if (m_isEmpty) + setSelectedIndex(-1); + } +} +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h new file mode 100644 index 00000000000..e30b16a5ff7 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h @@ -0,0 +1,50 @@ +// 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 +#include + +#include "modelnode.h" + +namespace QmlDesigner { + +class CollectionListModel : public QStringListModel +{ + Q_OBJECT + Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) + Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) + +public: + enum Roles { IdRole = Qt::UserRole + 1, NameRole, SourceRole, SelectedRole, CollectionsRole }; + + explicit CollectionListModel(const ModelNode &sourceModel); + virtual QHash roleNames() const override; + + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + Q_INVOKABLE int selectedIndex() const; + Q_INVOKABLE ModelNode sourceNode() const; + Q_INVOKABLE QString sourceAddress() const; + + void selectCollectionIndex(int idx, bool selectAtLeastOne = false); + QString collectionNameAt(int idx) const; + +signals: + void selectedIndexChanged(int idx); + void isEmptyChanged(bool); + +private: + void setSelectedIndex(int idx); + + void updateEmpty(); + + using Super = QStringListModel; + int m_selectedIndex = -1; + bool m_isEmpty = false; + const ModelNode m_sourceNode; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index eae5d645e16..999ebe449ba 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -1,33 +1,91 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + #include "collectionsourcemodel.h" #include "abstractview.h" +#include "collectioneditorconstants.h" +#include "collectionlistmodel.h" #include "variantproperty.h" #include +#include +#include +#include +#include + +namespace { + +QSharedPointer loadCollection( + const QmlDesigner::ModelNode &sourceNode, + QSharedPointer initialCollection = {}) +{ + using namespace QmlDesigner::CollectionEditor; + QString sourceFileAddress = sourceNode.variantProperty(SOURCEFILE_PROPERTY).value().toString(); + + QSharedPointer collectionsList; + auto setupCollectionList = [&sourceNode, &initialCollection, &collectionsList]() { + if (initialCollection.isNull()) + collectionsList.reset(new QmlDesigner::CollectionListModel(sourceNode)); + else if (initialCollection->sourceNode() == sourceNode) + collectionsList = initialCollection; + else + collectionsList.reset(new QmlDesigner::CollectionListModel(sourceNode)); + }; + + if (sourceNode.type() == JSONCOLLECTIONMODEL_TYPENAME) { + QFile sourceFile(sourceFileAddress); + if (!sourceFile.open(QFile::ReadOnly)) + return {}; + + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(sourceFile.readAll(), &parseError); + if (parseError.error != QJsonParseError::NoError) + return {}; + + setupCollectionList(); + + if (document.isObject()) { + const QJsonObject sourceObject = document.object(); + collectionsList->setStringList(sourceObject.toVariantMap().keys()); + } + } else if (sourceNode.type() == CSVCOLLECTIONMODEL_TYPENAME) { + QmlDesigner::VariantProperty collectionNameProperty = sourceNode.variantProperty( + "objectName"); + setupCollectionList(); + collectionsList->setStringList({collectionNameProperty.value().toString()}); + } + return collectionsList; +} +} // namespace + namespace QmlDesigner { + CollectionSourceModel::CollectionSourceModel() {} int CollectionSourceModel::rowCount(const QModelIndex &) const { - return m_collections.size(); + return m_collectionSources.size(); } QVariant CollectionSourceModel::data(const QModelIndex &index, int role) const { QTC_ASSERT(index.isValid(), return {}); - const ModelNode *collection = &m_collections.at(index.row()); + const ModelNode *collectionSource = &m_collectionSources.at(index.row()); switch (role) { case IdRole: - return collection->id(); + return collectionSource->id(); case NameRole: - return collection->variantProperty("objectName").value(); + return collectionSource->variantProperty("objectName").value(); + case SourceRole: + return collectionSource->variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value(); case SelectedRole: return index.row() == m_selectedIndex; + case CollectionsRole: + return QVariant::fromValue(m_collectionList.at(index.row()).data()); } return {}; @@ -38,30 +96,37 @@ bool CollectionSourceModel::setData(const QModelIndex &index, const QVariant &va if (!index.isValid()) return false; - ModelNode collection = m_collections.at(index.row()); + ModelNode collectionSource = m_collectionSources.at(index.row()); switch (role) { case IdRole: { - if (collection.id() == value) + if (collectionSource.id() == value) return false; - bool duplicatedId = Utils::anyOf(std::as_const(m_collections), - [&collection, &value](const ModelNode &otherCollection) { + bool duplicatedId = Utils::anyOf(std::as_const(m_collectionSources), + [&collectionSource, &value](const ModelNode &otherCollection) { return (otherCollection.id() == value - && otherCollection != collection); + && otherCollection != collectionSource); }); if (duplicatedId) return false; - collection.setIdWithRefactoring(value.toString()); + collectionSource.setIdWithRefactoring(value.toString()); } break; case Qt::DisplayRole: case NameRole: { - auto collectionName = collection.variantProperty("objectName"); + auto collectionName = collectionSource.variantProperty("objectName"); if (collectionName.value() == value) return false; collectionName.setValue(value.toString()); } break; + case SourceRole: { + auto sourceAddress = collectionSource.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY); + if (sourceAddress.value() == value) + return false; + + sourceAddress.setValue(value.toString()); + } break; case SelectedRole: { if (value.toBool() != index.data(SelectedRole).toBool()) setSelectedIndex(value.toBool() ? index.row() : -1); @@ -82,7 +147,7 @@ bool CollectionSourceModel::removeRows(int row, int count, [[maybe_unused]] cons if (row >= rowMax || row < 0) return false; - AbstractView *view = m_collections.at(row).view(); + AbstractView *view = m_collectionSources.at(row).view(); if (!view) return false; @@ -95,22 +160,22 @@ bool CollectionSourceModel::removeRows(int row, int count, [[maybe_unused]] cons beginRemoveRows({}, row, rowMax - 1); view->executeInTransaction(Q_FUNC_INFO, [row, count, this]() { - for (ModelNode node : Utils::span(m_collections).subspan(row, count)) { - m_collectionsIndexHash.remove(node.internalId()); + for (ModelNode node : Utils::span(m_collectionSources).subspan(row, count)) { + m_sourceIndexHash.remove(node.internalId()); node.destroy(); } + m_collectionSources.remove(row, count); + m_collectionList.remove(row, count); }); - m_collections.remove(row, count); - int idx = row; - for (const ModelNode &node : Utils::span(m_collections).subspan(row)) - m_collectionsIndexHash.insert(node.internalId(), ++idx); + for (const ModelNode &node : Utils::span(m_collectionSources).subspan(row)) + m_sourceIndexHash.insert(node.internalId(), ++idx); endRemoveRows(); if (selectionUpdateNeeded) - updateSelectedCollection(); + updateSelectedSource(); updateEmpty(); return true; @@ -121,82 +186,115 @@ QHash CollectionSourceModel::roleNames() const static QHash roles; if (roles.isEmpty()) { roles.insert(Super::roleNames()); - roles.insert({ - {IdRole, "collectionId"}, - {NameRole, "collectionName"}, - {SelectedRole, "collectionIsSelected"}, - }); + roles.insert({{IdRole, "sourceId"}, + {NameRole, "sourceName"}, + {SelectedRole, "sourceIsSelected"}, + {SourceRole, "sourceAddress"}, + {CollectionsRole, "collections"}}); } return roles; } -void CollectionSourceModel::setCollections(const ModelNodes &collections) +void CollectionSourceModel::setSources(const ModelNodes &sources) { beginResetModel(); - bool wasEmpty = isEmpty(); - m_collections = collections; - m_collectionsIndexHash.clear(); - int i = 0; - for (const ModelNode &collection : collections) - m_collectionsIndexHash.insert(collection.internalId(), i++); + m_collectionSources = sources; + m_sourceIndexHash.clear(); + m_collectionList.clear(); + int i = -1; + for (const ModelNode &collectionSource : sources) { + m_sourceIndexHash.insert(collectionSource.internalId(), ++i); - if (wasEmpty != isEmpty()) - emit isEmptyChanged(isEmpty()); + auto loadedCollection = loadCollection(collectionSource); + m_collectionList.append(loadedCollection); + connect(loadedCollection.data(), + &CollectionListModel::selectedIndexChanged, + this, + &CollectionSourceModel::onSelectedCollectionChanged, + Qt::UniqueConnection); + } + + updateEmpty(); endResetModel(); - updateSelectedCollection(true); + updateSelectedSource(true); } -void CollectionSourceModel::removeCollection(const ModelNode &node) +void CollectionSourceModel::removeSource(const ModelNode &node) { - int nodePlace = m_collectionsIndexHash.value(node.internalId(), -1); + int nodePlace = m_sourceIndexHash.value(node.internalId(), -1); if (nodePlace < 0) return; removeRow(nodePlace); } -int CollectionSourceModel::collectionIndex(const ModelNode &node) const +int CollectionSourceModel::sourceIndex(const ModelNode &node) const { - return m_collectionsIndexHash.value(node.internalId(), -1); + return m_sourceIndexHash.value(node.internalId(), -1); } -void CollectionSourceModel::selectCollection(const ModelNode &node) +void CollectionSourceModel::addSource(const ModelNode &node) { - int nodePlace = m_collectionsIndexHash.value(node.internalId(), -1); + int newRowId = m_collectionSources.count(); + beginInsertRows({}, newRowId, newRowId); + m_collectionSources.append(node); + m_sourceIndexHash.insert(node.internalId(), newRowId); + + auto loadedCollection = loadCollection(node); + m_collectionList.append(loadedCollection); + + connect(loadedCollection.data(), + &CollectionListModel::selectedIndexChanged, + this, + &CollectionSourceModel::onSelectedCollectionChanged, + Qt::UniqueConnection); + + updateEmpty(); + endInsertRows(); + updateSelectedSource(true); +} + +void CollectionSourceModel::selectSource(const ModelNode &node) +{ + int nodePlace = m_sourceIndexHash.value(node.internalId(), -1); if (nodePlace < 0) return; - selectCollectionIndex(nodePlace, true); + selectSourceIndex(nodePlace, true); } -QmlDesigner::ModelNode CollectionSourceModel::collectionNodeAt(int idx) +QmlDesigner::ModelNode CollectionSourceModel::sourceNodeAt(int idx) { QModelIndex data = index(idx); if (!data.isValid()) return {}; - return m_collections.at(idx); + return m_collectionSources.at(idx); } -bool CollectionSourceModel::isEmpty() const +CollectionListModel *CollectionSourceModel::selectedCollectionList() { - return m_collections.isEmpty(); + QModelIndex idx = index(m_selectedIndex); + if (!idx.isValid()) + return {}; + + return idx.data(CollectionsRole).value(); } -void CollectionSourceModel::selectCollectionIndex(int idx, bool selectAtLeastOne) +void CollectionSourceModel::selectSourceIndex(int idx, bool selectAtLeastOne) { - int collectionCount = m_collections.size(); - int prefferedIndex = -1; + int collectionCount = m_collectionSources.size(); + int preferredIndex = -1; if (collectionCount) { if (selectAtLeastOne) - prefferedIndex = std::max(0, std::min(idx, collectionCount - 1)); + preferredIndex = std::max(0, std::min(idx, collectionCount - 1)); else if (idx > -1 && idx < collectionCount) - prefferedIndex = idx; + preferredIndex = idx; } - setSelectedIndex(prefferedIndex); + setSelectedIndex(preferredIndex); } void CollectionSourceModel::deselect() @@ -204,17 +302,25 @@ void CollectionSourceModel::deselect() setSelectedIndex(-1); } -void CollectionSourceModel::updateSelectedCollection(bool selectAtLeastOne) +void CollectionSourceModel::updateSelectedSource(bool selectAtLeastOne) { int idx = m_selectedIndex; m_selectedIndex = -1; - selectCollectionIndex(idx, selectAtLeastOne); + selectSourceIndex(idx, selectAtLeastOne); } void CollectionSourceModel::updateNodeName(const ModelNode &node) { QModelIndex index = indexOfNode(node); emit dataChanged(index, index, {NameRole, Qt::DisplayRole}); + updateCollectionList(index); +} + +void CollectionSourceModel::updateNodeSource(const ModelNode &node) +{ + QModelIndex index = indexOfNode(node); + emit dataChanged(index, index, {SourceRole}); + updateCollectionList(index); } void CollectionSourceModel::updateNodeId(const ModelNode &node) @@ -223,9 +329,28 @@ void CollectionSourceModel::updateNodeId(const ModelNode &node) emit dataChanged(index, index, {IdRole}); } +QString CollectionSourceModel::selectedSourceAddress() const +{ + return index(m_selectedIndex).data(SourceRole).toString(); +} + +void CollectionSourceModel::onSelectedCollectionChanged(int collectionIndex) +{ + CollectionListModel *collectionList = qobject_cast(sender()); + if (collectionIndex > -1 && collectionList) { + if (_previousSelectedList && _previousSelectedList != collectionList) + _previousSelectedList->selectCollectionIndex(-1); + + emit collectionSelected(collectionList->sourceNode(), + collectionList->collectionNameAt(collectionIndex)); + + _previousSelectedList = collectionList; + } +} + void CollectionSourceModel::setSelectedIndex(int idx) { - idx = (idx > -1 && idx < m_collections.count()) ? idx : -1; + idx = (idx > -1 && idx < m_collectionSources.count()) ? idx : -1; if (m_selectedIndex != idx) { QModelIndex previousIndex = index(m_selectedIndex); @@ -245,7 +370,7 @@ void CollectionSourceModel::setSelectedIndex(int idx) void CollectionSourceModel::updateEmpty() { - bool isEmptyNow = isEmpty(); + bool isEmptyNow = m_collectionSources.isEmpty(); if (m_isEmpty != isEmptyNow) { m_isEmpty = isEmptyNow; emit isEmptyChanged(m_isEmpty); @@ -255,8 +380,22 @@ void CollectionSourceModel::updateEmpty() } } +void CollectionSourceModel::updateCollectionList(QModelIndex index) +{ + if (!index.isValid()) + return; + + ModelNode sourceNode = sourceNodeAt(index.row()); + QSharedPointer currentList = m_collectionList.at(index.row()); + QSharedPointer newList = loadCollection(sourceNode, currentList); + if (currentList != newList) { + m_collectionList.replace(index.row(), newList); + emit this->dataChanged(index, index, {CollectionsRole}); + } +} + QModelIndex CollectionSourceModel::indexOfNode(const ModelNode &node) const { - return index(m_collectionsIndexHash.value(node.internalId(), -1)); + return index(m_sourceIndexHash.value(node.internalId(), -1)); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h index cdde4362835..bd22d833ad9 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h @@ -1,5 +1,6 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + #pragma once #include "modelnode.h" @@ -7,20 +8,17 @@ #include #include -QT_BEGIN_NAMESPACE -class QJsonArray; -QT_END_NAMESPACE - namespace QmlDesigner { - +class CollectionListModel; class CollectionSourceModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) + Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) public: - enum Roles { IdRole = Qt::UserRole + 1, NameRole, SelectedRole }; + enum Roles { IdRole = Qt::UserRole + 1, NameRole, SourceRole, SelectedRole, CollectionsRole }; explicit CollectionSourceModel(); @@ -36,35 +34,44 @@ public: virtual QHash roleNames() const override; - void setCollections(const ModelNodes &collections); - void removeCollection(const ModelNode &node); - int collectionIndex(const ModelNode &node) const; - void selectCollection(const ModelNode &node); + void setSources(const ModelNodes &sources); + void removeSource(const ModelNode &node); + int sourceIndex(const ModelNode &node) const; + void addSource(const ModelNode &node); + void selectSource(const ModelNode &node); - ModelNode collectionNodeAt(int idx); + ModelNode sourceNodeAt(int idx); + CollectionListModel *selectedCollectionList(); - Q_INVOKABLE bool isEmpty() const; - Q_INVOKABLE void selectCollectionIndex(int idx, bool selectAtLeastOne = false); + Q_INVOKABLE void selectSourceIndex(int idx, bool selectAtLeastOne = false); Q_INVOKABLE void deselect(); - Q_INVOKABLE void updateSelectedCollection(bool selectAtLeastOne = false); + Q_INVOKABLE void updateSelectedSource(bool selectAtLeastOne = false); void updateNodeName(const ModelNode &node); + void updateNodeSource(const ModelNode &node); void updateNodeId(const ModelNode &node); + QString selectedSourceAddress() const; + signals: void selectedIndexChanged(int idx); - void renameCollectionTriggered(const QmlDesigner::ModelNode &collection, const QString &newName); - void addNewCollectionTriggered(); + void collectionSelected(const ModelNode &sourceNode, const QString &collectionName); void isEmptyChanged(bool); +private slots: + void onSelectedCollectionChanged(int collectionIndex); + private: void setSelectedIndex(int idx); void updateEmpty(); + void updateCollectionList(QModelIndex index); using Super = QAbstractListModel; QModelIndex indexOfNode(const ModelNode &node) const; - ModelNodes m_collections; - QHash m_collectionsIndexHash; // internalId -> index + ModelNodes m_collectionSources; + QHash m_sourceIndexHash; // internalId -> index + QList> m_collectionList; + QPointer _previousSelectedList; int m_selectedIndex = -1; bool m_isEmpty = true; }; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 2870e42db54..53cf76312d4 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -2,12 +2,13 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "collectionview.h" + +#include "collectioneditorconstants.h" #include "collectionsourcemodel.h" #include "collectionwidget.h" #include "designmodecontext.h" -#include "nodelistproperty.h" +#include "nodeabstractproperty.h" #include "nodemetainfo.h" -#include "qmldesignerconstants.h" #include "qmldesignerplugin.h" #include "singlecollectionmodel.h" #include "variantproperty.h" @@ -21,326 +22,21 @@ #include namespace { -using Data = std::variant; -using DataRecord = QMap; - -struct DataHeader +inline bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) { - enum class Type { Unknown, Bool, Numeric, String, DateTime }; - Type type; - QString name; -}; - -using DataHeaderMap = QMap; // Lowercase Name - Header Data - -inline constexpr QStringView BoolDataType{u"Bool"}; -inline constexpr QStringView NumberDataType{u"Number"}; -inline constexpr QStringView StringDataType{u"String"}; -inline constexpr QStringView DateTimeDataType{u"Date/Time"}; - -QString removeSpaces(QString string) -{ - string.replace(" ", "_"); - string.replace("-", "_"); - return string; -} - -DataHeader getDataType(const QString &type, const QString &name) -{ - static const QMap typeMap = { - {BoolDataType.toString().toLower(), DataHeader::Type::Bool}, - {NumberDataType.toString().toLower(), DataHeader::Type::Numeric}, - {StringDataType.toString().toLower(), DataHeader::Type::String}, - {DateTimeDataType.toString().toLower(), DataHeader::Type::DateTime}}; - if (name.isEmpty()) - return {}; - - if (type.isEmpty()) - return {DataHeader::Type::String, removeSpaces(name)}; - - return {typeMap.value(type.toLower(), DataHeader::Type::Unknown), removeSpaces(name)}; -} - -struct JsonDocumentError : public std::exception -{ - enum Error { - InvalidDocumentType, - InvalidCollectionName, - InvalidCollectionId, - InvalidCollectionObject, - InvalidArrayPosition, - InvalidLiteralType, - InvalidCollectionHeader, - IsNotJsonArray, - CollectionHeaderNotFound - }; - - const Error error; - - JsonDocumentError(Error error) - : error(error) - {} - - const char *what() const noexcept override - { - switch (error) { - case InvalidDocumentType: - return "Current JSON document contains errors."; - case InvalidCollectionName: - return "Invalid collection name."; - case InvalidCollectionId: - return "Invalid collection Id."; - case InvalidCollectionObject: - return "A collection should be a json object."; - case InvalidArrayPosition: - return "Arrays are not supported inside the collection."; - case InvalidLiteralType: - return "Invalid literal type for collection items"; - case InvalidCollectionHeader: - return "Invalid Collection Header"; - case IsNotJsonArray: - return "Json file should be an array"; - case CollectionHeaderNotFound: - return "Collection Header not found"; - default: - return "Unknown Json Error"; - } - } -}; - -struct CsvDocumentError : public std::exception -{ - enum Error { - HeaderNotFound, - DataNotFound, - }; - - const Error error; - - CsvDocumentError(Error error) - : error(error) - {} - - const char *what() const noexcept override - { - switch (error) { - case HeaderNotFound: - return "CSV Header not found"; - case DataNotFound: - return "CSV data not found"; - default: - return "Unknown CSV Error"; - } - } -}; - -Data getLiteralDataValue(const QVariant &value, const DataHeader &header, bool *typeWarningCheck = nullptr) -{ - if (header.type == DataHeader::Type::Bool) - return value.toBool(); - - if (header.type == DataHeader::Type::Numeric) - return value.toDouble(); - - if (header.type == DataHeader::Type::String) - return value.toString(); - - if (header.type == DataHeader::Type::DateTime) { - QDateTime dateTimeStr = QDateTime::fromString(value.toString()); - if (dateTimeStr.isValid()) - return dateTimeStr; - } - - if (typeWarningCheck) - *typeWarningCheck = true; - - return value.toString(); -} - -void loadJsonHeaders(QList &collectionHeaders, - DataHeaderMap &headerDataMap, - const QJsonObject &collectionJsonObject) -{ - const QJsonArray collectionHeader = collectionJsonObject.value("headers").toArray(); - for (const QJsonValue &headerValue : collectionHeader) { - const QJsonObject headerJsonObject = headerValue.toObject(); - DataHeader dataHeader = getDataType(headerJsonObject.value("type").toString(), - headerJsonObject.value("name").toString()); - - if (dataHeader.type == DataHeader::Type::Unknown) - throw JsonDocumentError{JsonDocumentError::InvalidCollectionHeader}; - - collectionHeaders.append(dataHeader); - headerDataMap.insert(dataHeader.name.toLower(), dataHeader); - } - - if (collectionHeaders.isEmpty()) - throw JsonDocumentError{JsonDocumentError::CollectionHeaderNotFound}; -} - -void loadJsonRecords(QList &collectionItems, - DataHeaderMap &headerDataMap, - const QJsonObject &collectionJsonObject) -{ - auto addItemFromValue = [&headerDataMap, &collectionItems](const QJsonValue &jsonValue) { - const QVariantMap dataMap = jsonValue.toObject().toVariantMap(); - DataRecord recordData; - for (const auto &dataPair : dataMap.asKeyValueRange()) { - const DataHeader correspondingHeader = headerDataMap.value(removeSpaces( - dataPair.first.toLower()), - {}); - - const QString &fieldName = correspondingHeader.name; - if (fieldName.size()) - recordData.insert(fieldName, - getLiteralDataValue(dataPair.second, correspondingHeader)); - } - if (!recordData.isEmpty()) - collectionItems.append(recordData); - }; - - const QJsonValue jsonDataValue = collectionJsonObject.value("data"); - if (jsonDataValue.isObject()) { - addItemFromValue(jsonDataValue); - } else if (jsonDataValue.isArray()) { - const QJsonArray jsonDataArray = jsonDataValue.toArray(); - for (const QJsonValue &jsonItem : jsonDataArray) { - if (jsonItem.isObject()) - addItemFromValue(jsonItem); - } - } -} - -inline bool isCollectionLib(const QmlDesigner::ModelNode &node) -{ - return node.parentProperty().parentModelNode().isRootNode() - && node.id() == QmlDesigner::Constants::COLLECTION_LIB_ID; -} - -inline bool isListModel(const QmlDesigner::ModelNode &node) -{ - return node.metaInfo().isQtQuickListModel(); -} - -inline bool isListElement(const QmlDesigner::ModelNode &node) -{ - return node.metaInfo().isQtQuickListElement(); -} - -inline bool isCollection(const QmlDesigner::ModelNode &node) -{ - return isCollectionLib(node.parentProperty().parentModelNode()) && isListModel(node); -} - -inline bool isCollectionElement(const QmlDesigner::ModelNode &node) -{ - return isListElement(node) && isCollection(node.parentProperty().parentModelNode()); + using namespace QmlDesigner::CollectionEditor; + return node.metaInfo().typeName() == JSONCOLLECTIONMODEL_TYPENAME + || node.metaInfo().typeName() == CSVCOLLECTIONMODEL_TYPENAME; } } // namespace namespace QmlDesigner { -struct Collection -{ - QString name; - QString id; - QList headers; - QList items; -}; - CollectionView::CollectionView(ExternalDependenciesInterface &externalDependencies) : AbstractView(externalDependencies) {} -bool CollectionView::loadJson(const QByteArray &data) -{ - try { - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(data, &parseError); - if (parseError.error != QJsonParseError::NoError) - throw JsonDocumentError{JsonDocumentError::InvalidDocumentType}; - - QList collections; - if (document.isArray()) { - const QJsonArray collectionsJsonArray = document.array(); - - for (const QJsonValue &collectionJson : collectionsJsonArray) { - Collection collection; - if (!collectionJson.isObject()) - throw JsonDocumentError{JsonDocumentError::InvalidCollectionObject}; - - QJsonObject collectionJsonObject = collectionJson.toObject(); - - const QString &collectionName = collectionJsonObject.value(u"name").toString(); - if (!collectionName.size()) - throw JsonDocumentError{JsonDocumentError::InvalidCollectionName}; - - const QString &collectionId = collectionJsonObject.value(u"id").toString(); - if (!collectionId.size()) - throw JsonDocumentError{JsonDocumentError::InvalidCollectionId}; - - DataHeaderMap headerDataMap; - - loadJsonHeaders(collection.headers, headerDataMap, collectionJsonObject); - loadJsonRecords(collection.items, headerDataMap, collectionJsonObject); - - if (collection.items.count()) - collections.append(collection); - } - } else { - throw JsonDocumentError{JsonDocumentError::InvalidDocumentType}; - } - - addLoadedModel(collections); - } catch (const std::exception &error) { - m_widget->warn("Json Import Problem", QString::fromLatin1(error.what())); - return false; - } - - return true; -} - -bool CollectionView::loadCsv(const QString &collectionName, const QByteArray &data) -{ - QTextStream stream(data); - Collection collection; - collection.name = collectionName; - - try { - if (!stream.atEnd()) { - const QStringList recordData = stream.readLine().split(','); - for (const QString &name : recordData) - collection.headers.append(getDataType({}, name)); - } - if (collection.headers.isEmpty()) - throw CsvDocumentError{CsvDocumentError::HeaderNotFound}; - - while (!stream.atEnd()) { - const QStringList recordDataList = stream.readLine().split(','); - DataRecord recordData; - int column = -1; - for (const QString &cellData : recordDataList) { - if (++column == collection.headers.size()) - break; - recordData.insert(collection.headers.at(column).name, cellData); - } - if (recordData.count()) - collection.items.append(recordData); - } - - if (collection.items.isEmpty()) - throw CsvDocumentError{CsvDocumentError::DataNotFound}; - - addLoadedModel({collection}); - } catch (const std::exception &error) { - m_widget->warn("Json Import Problem", QString::fromLatin1(error.what())); - return false; - } - - return true; -} - bool CollectionView::hasWidget() const { return true; @@ -355,10 +51,12 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() Core::ICore::addContextObject(collectionEditorContext); CollectionSourceModel *sourceModel = m_widget->sourceModel().data(); - connect(sourceModel, &CollectionSourceModel::selectedIndexChanged, this, [&](int selectedIndex) { - m_widget->singleCollectionModel()->setCollection( - m_widget->sourceModel()->collectionNodeAt(selectedIndex)); - }); + connect(sourceModel, + &CollectionSourceModel::collectionSelected, + this, + [this](const ModelNode &sourceNode, const QString &collection) { + m_widget->singleCollectionModel()->loadCollection(sourceNode, collection); + }); } return createWidgetInfo(m_widget.data(), @@ -376,47 +74,31 @@ void CollectionView::modelAttached(Model *model) } void CollectionView::nodeReparented(const ModelNode &node, - const NodeAbstractProperty &newPropertyParent, - const NodeAbstractProperty &oldPropertyParent, + [[maybe_unused]] const NodeAbstractProperty &newPropertyParent, + [[maybe_unused]] const NodeAbstractProperty &oldPropertyParent, [[maybe_unused]] PropertyChangeFlags propertyChange) { - if (!isListModel(node)) - return; - - ModelNode newParentNode = newPropertyParent.parentModelNode(); - ModelNode oldParentNode = oldPropertyParent.parentModelNode(); - bool added = isCollectionLib(newParentNode); - bool removed = isCollectionLib(oldParentNode); - - if (!added && !removed) + if (!isStudioCollectionModel(node)) return; refreshModel(); - if (isCollection(node)) - m_widget->sourceModel()->selectCollection(node); + m_widget->sourceModel()->selectSource(node); } void CollectionView::nodeAboutToBeRemoved(const ModelNode &removedNode) { // removing the collections lib node - if (isCollectionLib(removedNode)) { - m_widget->sourceModel()->setCollections({}); - return; - } - - if (isCollection(removedNode)) - m_widget->sourceModel()->removeCollection(removedNode); + if (isStudioCollectionModel(removedNode)) + m_widget->sourceModel()->removeSource(removedNode); } -void CollectionView::nodeRemoved([[maybe_unused]] const ModelNode &removedNode, - const NodeAbstractProperty &parentProperty, +void CollectionView::nodeRemoved(const ModelNode &removedNode, + [[maybe_unused]] const NodeAbstractProperty &parentProperty, [[maybe_unused]] PropertyChangeFlags propertyChange) { - if (parentProperty.parentModelNode().id() != Constants::COLLECTION_LIB_ID) - return; - - m_widget->sourceModel()->updateSelectedCollection(true); + if (isStudioCollectionModel(removedNode)) + m_widget->sourceModel()->updateSelectedSource(true); } void CollectionView::variantPropertiesChanged(const QList &propertyList, @@ -424,9 +106,11 @@ void CollectionView::variantPropertiesChanged(const QList &prop { for (const VariantProperty &property : propertyList) { ModelNode node(property.parentModelNode()); - if (isCollection(node)) { + if (isStudioCollectionModel(node)) { if (property.name() == "objectName") m_widget->sourceModel()->updateNodeName(node); + else if (property.name() == CollectionEditor::SOURCEFILE_PROPERTY) + m_widget->sourceModel()->updateNodeSource(node); else if (property.name() == "id") m_widget->sourceModel()->updateNodeId(node); } @@ -436,50 +120,36 @@ void CollectionView::variantPropertiesChanged(const QList &prop void CollectionView::selectedNodesChanged(const QList &selectedNodeList, [[maybe_unused]] const QList &lastSelectedNodeList) { - QList selectedCollections = Utils::filtered(selectedNodeList, &isCollection); + QList selectedJsonCollections = Utils::filtered(selectedNodeList, + &isStudioCollectionModel); // More than one collections are selected. So ignore them - if (selectedCollections.size() > 1) + if (selectedJsonCollections.size() > 1) return; - if (selectedCollections.size() == 1) { // If exactly one collection is selected - m_widget->sourceModel()->selectCollection(selectedCollections.first()); + if (selectedJsonCollections.size() == 1) { // If exactly one collection is selected + m_widget->sourceModel()->selectSource(selectedJsonCollections.first()); return; } - - // If no collection is selected, check the elements - QList selectedElements = Utils::filtered(selectedNodeList, &isCollectionElement); - if (selectedElements.size()) { - const ModelNode parentElement = selectedElements.first().parentProperty().parentModelNode(); - bool haveSameParent = Utils::allOf(selectedElements, [&parentElement](const ModelNode &element) { - return element.parentProperty().parentModelNode() == parentElement; - }); - if (haveSameParent) - m_widget->sourceModel()->selectCollection(parentElement); - } } -void CollectionView::addNewCollection(const QString &name) +void CollectionView::addResource(const QUrl &url, const QString &name, const QString &type) { - executeInTransaction(__FUNCTION__, [&] { - ensureCollectionLibraryNode(); - ModelNode collectionLib = collectionLibraryNode(); - if (!collectionLib.isValid()) - return; - - NodeMetaInfo listModelMetaInfo = model()->qtQmlModelsListModelMetaInfo(); - ModelNode collectionNode = createModelNode(listModelMetaInfo.typeName(), - listModelMetaInfo.majorVersion(), - listModelMetaInfo.minorVersion()); - QString collectionName = name.isEmpty() ? "Collection" : name; - renameCollection(collectionNode, collectionName); - - QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED); - - auto headersProperty = collectionNode.variantProperty("headers"); - headersProperty.setDynamicTypeNameAndValue("string", {}); - - collectionLib.defaultNodeListProperty().reparentHere(collectionNode); + executeInTransaction(Q_FUNC_INFO, [this, &url, &name, &type]() { + ensureStudioModelImport(); + QString sourceAddress = url.isLocalFile() ? url.toLocalFile() : url.toString(); + const NodeMetaInfo resourceMetaInfo = type.compare("json", Qt::CaseInsensitive) == 0 + ? jsonCollectionMetaInfo() + : csvCollectionMetaInfo(); + ModelNode resourceNode = createModelNode(resourceMetaInfo.typeName(), + resourceMetaInfo.majorVersion(), + resourceMetaInfo.minorVersion()); + VariantProperty sourceProperty = resourceNode.variantProperty( + CollectionEditor::SOURCEFILE_PROPERTY); + VariantProperty nameProperty = resourceNode.variantProperty("objectName"); + sourceProperty.setValue(sourceAddress); + nameProperty.setValue(name); + rootModelNode().defaultNodeAbstractProperty().reparentHere(resourceNode); }); } @@ -488,118 +158,32 @@ void CollectionView::refreshModel() if (!model()) return; - ModelNode collectionLib = modelNodeForId(Constants::COLLECTION_LIB_ID); - ModelNodes collections; - - if (collectionLib.isValid()) { - const QList collectionLibNodes = collectionLib.directSubModelNodes(); - for (const ModelNode &node : collectionLibNodes) { - if (isCollection(node)) - collections.append(node); - } - } - - m_widget->sourceModel()->setCollections(collections); + // Load Json Collections + const ModelNodes jsonSourceNodes = rootModelNode().subModelNodesOfType(jsonCollectionMetaInfo()); + m_widget->sourceModel()->setSources(jsonSourceNodes); } -ModelNode CollectionView::getNewCollectionNode(const Collection &collection) +NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const { - QTC_ASSERT(model(), return {}); - ModelNode collectionNode; - executeInTransaction(__FUNCTION__, [&] { - NodeMetaInfo listModelMetaInfo = model()->qtQmlModelsListModelMetaInfo(); - collectionNode = createModelNode(listModelMetaInfo.typeName(), - listModelMetaInfo.majorVersion(), - listModelMetaInfo.minorVersion()); - QString collectionName = collection.name.isEmpty() ? "Collection" : collection.name; - renameCollection(collectionNode, collectionName); - QStringList headers; - for (const DataHeader &header : collection.headers) - headers.append(header.name); - - QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED); - - auto headersProperty = collectionNode.variantProperty("headers"); - headersProperty.setDynamicTypeNameAndValue("string", headers.join(",")); - - NodeMetaInfo listElementMetaInfo = model()->qtQmlModelsListElementMetaInfo(); - for (const DataRecord &item : collection.items) { - ModelNode elementNode = createModelNode(listElementMetaInfo.typeName(), - listElementMetaInfo.majorVersion(), - listElementMetaInfo.minorVersion()); - for (const auto &headerMapElement : item.asKeyValueRange()) { - auto property = elementNode.variantProperty(headerMapElement.first.toLatin1()); - QVariant value = std::visit([](const auto &data) - -> QVariant { return QVariant::fromValue(data); }, - headerMapElement.second); - property.setValue(value); - } - collectionNode.defaultNodeListProperty().reparentHere(elementNode); - } - }); - return collectionNode; + return model()->metaInfo(CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME); } -void CollectionView::addLoadedModel(const QList &newCollection) +NodeMetaInfo CollectionView::csvCollectionMetaInfo() const +{ + return model()->metaInfo(CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME); +} + +void CollectionView::ensureStudioModelImport() { executeInTransaction(__FUNCTION__, [&] { - ensureCollectionLibraryNode(); - ModelNode collectionLib = collectionLibraryNode(); - if (!collectionLib.isValid()) - return; - - for (const Collection &collection : newCollection) { - ModelNode collectionNode = getNewCollectionNode(collection); - collectionLib.defaultNodeListProperty().reparentHere(collectionNode); + Import import = Import::createLibraryImport(CollectionEditor::COLLECTIONMODEL_IMPORT); + try { + if (!model()->hasImport(import, true, true)) + model()->changeImports({import}, {}); + } catch (const Exception &) { + QTC_ASSERT(false, return); } }); } -void CollectionView::renameCollection(ModelNode &collection, const QString &newName) -{ - QTC_ASSERT(collection.isValid(), return); - - QVariant objName = collection.variantProperty("objectName").value(); - if (objName.isValid() && objName.toString() == newName) - return; - - executeInTransaction(__FUNCTION__, [&] { - collection.setIdWithRefactoring(model()->generateIdFromName(newName, "collection")); - - VariantProperty objNameProp = collection.variantProperty("objectName"); - objNameProp.setValue(newName); - }); -} - -void CollectionView::ensureCollectionLibraryNode() -{ - ModelNode collectionLib = modelNodeForId(Constants::COLLECTION_LIB_ID); - if (collectionLib.isValid() - || (!rootModelNode().metaInfo().isQtQuick3DNode() - && !rootModelNode().metaInfo().isQtQuickItem())) { - return; - } - - executeInTransaction(__FUNCTION__, [&] { - // Create collection library node -#ifdef QDS_USE_PROJECTSTORAGE - TypeName nodeTypeName = rootModelNode().metaInfo().isQtQuick3DNode() ? "Node" : "Item"; - collectionLib = createModelNode(nodeTypeName, -1, -1); -#else - auto nodeType = rootModelNode().metaInfo().isQtQuick3DNode() - ? model()->qtQuick3DNodeMetaInfo() - : model()->qtQuickItemMetaInfo(); - collectionLib = createModelNode(nodeType.typeName(), - nodeType.majorVersion(), - nodeType.minorVersion()); -#endif - collectionLib.setIdWithoutRefactoring(Constants::COLLECTION_LIB_ID); - rootModelNode().defaultNodeListProperty().reparentHere(collectionLib); - }); -} - -ModelNode CollectionView::collectionLibraryNode() -{ - return modelNodeForId(Constants::COLLECTION_LIB_ID); -} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index 9372c74150f..6d81d00f064 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -1,5 +1,6 @@ // 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 "abstractview.h" @@ -9,7 +10,6 @@ namespace QmlDesigner { -struct Collection; class CollectionWidget; class CollectionView : public AbstractView @@ -19,9 +19,6 @@ class CollectionView : public AbstractView public: explicit CollectionView(ExternalDependenciesInterface &externalDependencies); - bool loadJson(const QByteArray &data); - bool loadCsv(const QString &collectionName, const QByteArray &data); - bool hasWidget() const override; WidgetInfo widgetInfo() override; @@ -44,15 +41,13 @@ public: void selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) override; - void addNewCollection(const QString &name); + void addResource(const QUrl &url, const QString &name, const QString &type); private: void refreshModel(); - ModelNode getNewCollectionNode(const Collection &collection); - void addLoadedModel(const QList &newCollection); - void renameCollection(ModelNode &material, const QString &newName); - void ensureCollectionLibraryNode(); - ModelNode collectionLibraryNode(); + NodeMetaInfo jsonCollectionMetaInfo() const; + NodeMetaInfo csvCollectionMetaInfo() const; + void ensureStudioModelImport(); QPointer m_widget; }; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 4bbc1d21e42..c4d9631cefe 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -103,26 +104,23 @@ void CollectionWidget::reloadQmlSource() bool CollectionWidget::loadJsonFile(const QString &jsonFileAddress) { - QUrl jsonUrl(jsonFileAddress); - QString fileAddress = jsonUrl.isLocalFile() ? jsonUrl.toLocalFile() : jsonUrl.toString(); - QFile file(fileAddress); - if (file.open(QFile::ReadOnly)) - return m_view->loadJson(file.readAll()); + if (!isJsonFile(jsonFileAddress)) + return false; - warn("Unable to open the file", file.errorString()); - return false; + QUrl jsonUrl(jsonFileAddress); + QFileInfo fileInfo(jsonUrl.isLocalFile() ? jsonUrl.toLocalFile() : jsonUrl.toString()); + + m_view->addResource(jsonUrl, fileInfo.completeBaseName(), "json"); + + return true; } bool CollectionWidget::loadCsvFile(const QString &collectionName, const QString &csvFileAddress) { QUrl csvUrl(csvFileAddress); - QString fileAddress = csvUrl.isLocalFile() ? csvUrl.toLocalFile() : csvUrl.toString(); - QFile file(fileAddress); - if (file.open(QFile::ReadOnly)) - return m_view->loadCsv(collectionName, file.readAll()); + m_view->addResource(csvUrl, collectionName, "csv"); - warn("Unable to open the file", file.errorString()); - return false; + return true; } bool CollectionWidget::isJsonFile(const QString &jsonFileAddress) const @@ -155,10 +153,10 @@ bool CollectionWidget::isCsvFile(const QString &csvFileAddress) const return true; } -bool CollectionWidget::addCollection(const QString &collectionName) const +bool CollectionWidget::addCollection([[maybe_unused]] const QString &collectionName) const { - m_view->addNewCollection(collectionName); - return true; + // TODO + return false; } void CollectionWidget::warn(const QString &title, const QString &body) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index 55f0d3b9587..de2b4d8d9fc 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -1,5 +1,6 @@ // 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 diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index 040f9307b67..62a95474a55 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -3,31 +3,36 @@ #include "singlecollectionmodel.h" -#include "nodemetainfo.h" +#include "collectioneditorconstants.h" +#include "modelnode.h" #include "variantproperty.h" #include -namespace { -inline bool isListElement(const QmlDesigner::ModelNode &node) -{ - return node.metaInfo().isQtQuickListElement(); -} +#include +#include +#include -inline QByteArrayList getHeaders(const QByteArray &headersValue) +namespace { + +QStringList getJsonHeaders(const QJsonArray &collectionArray) { - QByteArrayList result; - const QByteArrayList initialHeaders = headersValue.split(','); - for (QByteArray header : initialHeaders) { - header = header.trimmed(); - if (header.size()) - result.append(header); + QSet result; + for (const QJsonValue &value : collectionArray) { + if (value.isObject()) { + const QJsonObject object = value.toObject(); + const QStringList headers = object.toVariantMap().keys(); + for (const QString &header : headers) + result.insert(header); + } } - return result; + + return result.values(); } } // namespace namespace QmlDesigner { + SingleCollectionModel::SingleCollectionModel(QObject *parent) : QAbstractTableModel(parent) {} @@ -47,11 +52,11 @@ QVariant SingleCollectionModel::data(const QModelIndex &index, int) const if (!index.isValid()) return {}; - const QByteArray &propertyName = m_headers.at(index.column()); - const ModelNode &elementNode = m_elements.at(index.row()); + const QString &propertyName = m_headers.at(index.column()); + const QJsonObject &elementNode = m_elements.at(index.row()); - if (elementNode.hasVariantProperty(propertyName)) - return elementNode.variantProperty(propertyName).value(); + if (elementNode.contains(propertyName)) + return elementNode.value(propertyName).toVariant(); return {}; } @@ -79,32 +84,110 @@ QVariant SingleCollectionModel::headerData(int section, return {}; } -void SingleCollectionModel::setCollection(const ModelNode &collection) +void SingleCollectionModel::loadCollection(const ModelNode &sourceNode, const QString &collection) +{ + QString fileName = sourceNode.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value().toString(); + + if (sourceNode.type() == CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) + loadJsonCollection(fileName, collection); + else if (sourceNode.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME) + loadCsvCollection(fileName, collection); +} + +void SingleCollectionModel::loadJsonCollection(const QString &source, const QString &collection) { beginResetModel(); - m_collectionNode = collection; - updateCollectionName(); + setCollectionName(collection); + QFile sourceFile(source); + QJsonArray collectionNodes; + bool jsonFileIsOk = false; + if (sourceFile.open(QFile::ReadOnly)) { + QJsonParseError jpe; + QJsonDocument document = QJsonDocument::fromJson(sourceFile.readAll(), &jpe); + if (jpe.error == QJsonParseError::NoError) { + jsonFileIsOk = true; + if (document.isObject()) { + QJsonObject collectionMap = document.object(); + if (collectionMap.contains(collection)) { + QJsonValue collectionVal = collectionMap.value(collection); + if (collectionVal.isArray()) + collectionNodes = collectionVal.toArray(); + else + collectionNodes.append(collectionVal); + } + } + } + } - QTC_ASSERT(collection.isValid() && collection.hasVariantProperty("headers"), { + setCollectionSourceFormat(jsonFileIsOk ? SourceFormat::Json : SourceFormat::Unknown); + + if (collectionNodes.isEmpty()) { m_headers.clear(); m_elements.clear(); endResetModel(); return; - }); + } + + m_headers = getJsonHeaders(collectionNodes); + + m_elements.clear(); + for (const QJsonValue &value : std::as_const(collectionNodes)) { + if (value.isObject()) { + QJsonObject object = value.toObject(); + m_elements.append(object); + } + } - m_headers = getHeaders(collection.variantProperty("headers").value().toByteArray()); - m_elements = Utils::filtered(collection.allSubModelNodes(), &isListElement); endResetModel(); } -void SingleCollectionModel::updateCollectionName() +void SingleCollectionModel::loadCsvCollection(const QString &source, const QString &collectionName) +{ + beginResetModel(); + + setCollectionName(collectionName); + QFile sourceFile(source); + m_headers.clear(); + m_elements.clear(); + bool csvFileIsOk = false; + + if (sourceFile.open(QFile::ReadOnly)) { + QTextStream stream(&sourceFile); + + if (!stream.atEnd()) + m_headers = stream.readLine().split(','); + + if (!m_headers.isEmpty()) { + while (!stream.atEnd()) { + const QStringList recordDataList = stream.readLine().split(','); + int column = -1; + QJsonObject recordData; + for (const QString &cellData : recordDataList) { + if (++column == m_headers.size()) + break; + recordData.insert(m_headers.at(column), cellData); + } + if (recordData.count()) + m_elements.append(recordData); + } + csvFileIsOk = true; + } + } + + setCollectionSourceFormat(csvFileIsOk ? SourceFormat::Csv : SourceFormat::Unknown); + endResetModel(); +} + +void SingleCollectionModel::setCollectionName(const QString &newCollectionName) { - QString newCollectionName = m_collectionNode.isValid() - ? m_collectionNode.variantProperty("objectName").value().toString() - : ""; if (m_collectionName != newCollectionName) { m_collectionName = newCollectionName; emit this->collectionNameChanged(m_collectionName); } } + +void SingleCollectionModel::setCollectionSourceFormat(SourceFormat sourceFormat) +{ + m_sourceFormat = sourceFormat; +} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h index 8d2c6c41b4f..471e43b9674 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -1,16 +1,15 @@ // 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 "modelnode.h" - #include - -QT_BEGIN_NAMESPACE -class QJsonArray; -QT_END_NAMESPACE +#include namespace QmlDesigner { + +class ModelNode; + class SingleCollectionModel : public QAbstractTableModel { Q_OBJECT @@ -18,6 +17,7 @@ class SingleCollectionModel : public QAbstractTableModel Q_PROPERTY(QString collectionName MEMBER m_collectionName NOTIFY collectionNameChanged) public: + enum class SourceFormat { Unknown, Json, Csv }; explicit SingleCollectionModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent) const override; @@ -29,18 +29,21 @@ public: Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - void setCollection(const ModelNode &collection); + void loadCollection(const ModelNode &sourceNode, const QString &collection); signals: void collectionNameChanged(const QString &collectionName); private: - void updateCollectionName(); + void setCollectionName(const QString &newCollectionName); + void setCollectionSourceFormat(SourceFormat sourceFormat); + void loadJsonCollection(const QString &source, const QString &collection); + void loadCsvCollection(const QString &source, const QString &collectionName); - QByteArrayList m_headers; - ModelNodes m_elements; - ModelNode m_collectionNode; + QStringList m_headers; + QList m_elements; QString m_collectionName; + SourceFormat m_sourceFormat = SourceFormat::Unknown; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 60e1ded8474..ec29c3726a9 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -78,7 +78,6 @@ const char QUICK_3D_ASSET_IMPORT_DATA_OPTIONS_KEY[] = "import_options"; const char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene"; const char DEFAULT_ASSET_IMPORT_FOLDER[] = "/asset_imports"; const char MATERIAL_LIB_ID[] = "__materialLibrary__"; -const char COLLECTION_LIB_ID[] = "__collectionLibrary__"; const char MIME_TYPE_ITEM_LIBRARY_INFO[] = "application/vnd.qtdesignstudio.itemlibraryinfo"; const char MIME_TYPE_ASSETS[] = "application/vnd.qtdesignstudio.assets"; From 44a8ffdf399887942c963c55078465db40bcea2b Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 2 Oct 2023 11:33:15 +0300 Subject: [PATCH 107/130] QmlDesigner: Refactor effect preview component Warm up rendering by introducing some adjusts and refactoring Change-Id: I6575467325f5f6c2001958d4f95c9c74be17143f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mahmoud Badri --- .../EffectMakerPreview.qml | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index 326c6017816..c10a8fd4720 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -16,6 +16,8 @@ Column { required property Item mainRoot + property alias source: source + Rectangle { // toolbar width: parent.width height: StudioTheme.Values.toolbarHeight @@ -38,35 +40,35 @@ Column { } HelperWidgets.AbstractButton { - enabled: previewImage.scale > .4 + enabled: sourceImage.scale > .4 style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.zoomOut_medium tooltip: qsTr("Zoom out") onClicked: { - previewImage.scale -= .2 + sourceImage.scale -= .2 } } HelperWidgets.AbstractButton { - enabled: previewImage.scale < 2 + enabled: sourceImage.scale < 2 style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.zoomIn_medium tooltip: qsTr("Zoom In") onClicked: { - previewImage.scale += .2 + sourceImage.scale += .2 } } HelperWidgets.AbstractButton { - enabled: previewImage.scale !== 1 + enabled: sourceImage.scale !== 1 style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.fitAll_medium tooltip: qsTr("Zoom Fit") onClicked: { - previewImage.scale = 1 + sourceImage.scale = 1 } } @@ -107,27 +109,33 @@ Column { } Rectangle { // preview image - id: previewImageBg + id: preview color: "#dddddd" width: parent.width height: 200 clip: true - Image { - id: previewImage - - anchors.margins: 5 + Item { + id: source anchors.fill: parent - fillMode: Image.PreserveAspectFit - smooth: true + layer.enabled: true + layer.mipmap: true + layer.smooth: true - source: imagesComboBox.selectedImage + Image { + id: sourceImage + anchors.margins: 5 + anchors.fill: parent + fillMode: Image.PreserveAspectFit + source: imagesComboBox.selectedImage + smooth: true - Behavior on scale { - NumberAnimation { - duration: 200 - easing.type: Easing.OutQuad + Behavior on scale { + NumberAnimation { + duration: 200 + easing.type: Easing.OutQuad + } } } } From dd47104d200a9642d446899b615393604340d612 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 2 Oct 2023 14:20:11 +0300 Subject: [PATCH 108/130] QmlDesigner: Create the host component for effects A dummy parent to host the effect qml object. Task-number: QDS-10811 Change-Id: I448e7d334070011d2d22a6a2cd43262b6fc57ea2 Reviewed-by: Mahmoud Badri --- .../EffectMakerPreview.qml | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index c10a8fd4720..f2b352ba4d1 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -15,7 +15,7 @@ Column { width: parent.width required property Item mainRoot - + property var effectMakerModel: EffectMakerBackend.effectMakerModel property alias source: source Rectangle { // toolbar @@ -116,7 +116,7 @@ Column { height: 200 clip: true - Item { + Item { // Source item as a canvas (render target) for effect id: source anchors.fill: parent layer.enabled: true @@ -139,5 +139,52 @@ Column { } } } + + Item { + id: componentParent + width: source.width + height: source.height + anchors.centerIn: parent + scale: 1 //TODO should come from toolbar + // Cache the layer. This way heavy shaders rendering doesn't + // slow down code editing & rest of the UI. + layer.enabled: true + layer.smooth: true + } + + // Create a dummy parent to host the effect qml object + function createNewComponent() { + var oldComponent = componentParent.children[0]; + if (oldComponent) + oldComponent.destroy(); + + try { + const newObject = Qt.createQmlObject( + effectMakerModel.qmlComponentString, //TODO + componentParent, + "" + ); + effectMakerModel.resetEffectError(0); + } catch (error) { + let errorString = "QML: ERROR: "; + let errorLine = -1; + if (error.qmlErrors.length > 0) { + // Show the first QML error + let e = error.qmlErrors[0]; + errorString += e.lineNumber + ": " + e.message; + errorLine = e.lineNumber; + } + effectMakerModel.setEffectError(errorString, 0, errorLine); + } + } + + Timer { + id: updateTimer + interval: effectMakerModel.effectUpdateDelay(); //TODO + onTriggered: { + effectMakerModel.updateQmlComponent(); //TODO + createNewComponent(); + } + } } } From 00cef940c0836547d1e4f964e77daff171e21428 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 28 Sep 2023 16:21:38 +0300 Subject: [PATCH 109/130] QmlDesigner: Sync also skybox for scene environment in 3D view If scene environment sync is specified for the 3D view background, we now sync also skybox instead of just the clear color. Fixes: QDS-10775 Change-Id: I3e8bd3b8155a4fbe476ca348761d56955a62f7c4 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../interfaces/nodeinstanceglobal.h | 2 +- .../components/edit3d/edit3dview.cpp | 62 +++++++------ .../components/edit3d/edit3dview.h | 8 +- .../qmldesigner/qmldesignerconstants.h | 2 +- .../qml2puppet/mockfiles/qt6/EditView3D.qml | 55 ++++++++--- .../qml2puppet/mockfiles/qt6/SceneView3D.qml | 1 + .../qml2puppet/editor3d/generalhelper.cpp | 57 +++++++++++- .../qml2puppet/editor3d/generalhelper.h | 21 ++++- .../qt5informationnodeinstanceserver.cpp | 92 +++++++++++-------- .../qt5informationnodeinstanceserver.h | 5 +- 10 files changed, 206 insertions(+), 99 deletions(-) diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index 6e25b3b419c..58166516c80 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -44,7 +44,7 @@ enum class View3DActionType { ParticlesPlay, ParticlesRestart, ParticlesSeek, - SyncBackgroundColor, + SyncEnvBackground, GetNodeAtPos, SetBakeLightsView3D }; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index bb1b3e74985..fc81eef4797 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -120,7 +120,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) const QString cameraFrustumKey = QStringLiteral("showCameraFrustum"); const QString particleEmitterKey = QStringLiteral("showParticleEmitter"); const QString particlesPlayKey = QStringLiteral("particlePlay"); - const QString syncBgColorKey = QStringLiteral("syncBackgroundColor"); + const QString syncEnvBgKey = QStringLiteral("syncEnvBackground"); if (sceneState.contains(sceneKey)) { qint32 newActiveScene = sceneState[sceneKey].value(); @@ -195,9 +195,11 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) bool syncValue = false; bool syncEnabled = false; bool desiredSyncValue = false; - if (sceneState.contains(syncBgColorKey)) - desiredSyncValue = sceneState[syncBgColorKey].toBool(); + if (sceneState.contains(syncEnvBgKey)) + desiredSyncValue = sceneState[syncEnvBgKey].toBool(); ModelNode checkNode = active3DSceneNode(); + const bool activeSceneValid = checkNode.isValid(); + while (checkNode.isValid()) { if (checkNode.metaInfo().isQtQuick3DView3D()) { syncValue = desiredSyncValue; @@ -210,15 +212,15 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) break; } - if (syncValue != desiredSyncValue) { + if (activeSceneValid && syncValue != desiredSyncValue) { // Update actual toolstate as well if we overrode it. QTimer::singleShot(0, this, [this, syncValue]() { - emitView3DAction(View3DActionType::SyncBackgroundColor, syncValue); + emitView3DAction(View3DActionType::SyncEnvBackground, syncValue); }); } - m_syncBackgroundColorAction->action()->setChecked(syncValue); - m_syncBackgroundColorAction->action()->setEnabled(syncEnabled); + m_syncEnvBackgroundAction->action()->setChecked(syncValue); + m_syncEnvBackgroundAction->action()->setEnabled(syncEnabled); // Selection context change updates visible and enabled states SelectionContext selectionContext(this); @@ -449,23 +451,23 @@ QSize Edit3DView::canvasSize() const return {}; } -void Edit3DView::createSelectBackgroundColorAction(QAction *syncBackgroundColorAction) +void Edit3DView::createSelectBackgroundColorAction(QAction *syncEnvBackgroundAction) { QString description = QCoreApplication::translate("SelectBackgroundColorAction", "Select Background Color"); QString tooltip = QCoreApplication::translate("SelectBackgroundColorAction", "Select a color for the background of the 3D view."); - auto operation = [this, syncBackgroundColorAction](const SelectionContext &) { + auto operation = [this, syncEnvBackgroundAction](const SelectionContext &) { BackgroundColorSelection::showBackgroundColorSelectionWidget( edit3DWidget(), DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, this, edit3dBgColorProperty, - [this, syncBackgroundColorAction]() { - if (syncBackgroundColorAction->isChecked()) { - emitView3DAction(View3DActionType::SyncBackgroundColor, false); - syncBackgroundColorAction->setChecked(false); + [this, syncEnvBackgroundAction]() { + if (syncEnvBackgroundAction->isChecked()) { + emitView3DAction(View3DActionType::SyncEnvBackground, false); + syncEnvBackgroundAction->setChecked(false); } }); }; @@ -510,14 +512,14 @@ void Edit3DView::createGridColorSelectionAction() tooltip); } -void Edit3DView::createResetColorAction(QAction *syncBackgroundColorAction) +void Edit3DView::createResetColorAction(QAction *syncEnvBackgroundAction) { QString description = QCoreApplication::translate("ResetEdit3DColorsAction", "Reset Colors"); QString tooltip = QCoreApplication::translate("ResetEdit3DColorsAction", "Reset the background color and the color of the " "grid lines of the 3D view to the default values."); - auto operation = [this, syncBackgroundColorAction](const SelectionContext &) { + auto operation = [this, syncEnvBackgroundAction](const SelectionContext &) { QList bgColors = {QRgb(0x222222), QRgb(0x999999)}; Edit3DViewConfig::setColors(this, edit3dBgColorProperty, bgColors); Edit3DViewConfig::saveColors(DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, bgColors); @@ -526,9 +528,9 @@ void Edit3DView::createResetColorAction(QAction *syncBackgroundColorAction) Edit3DViewConfig::setColors(this, edit3dGridColorProperty, {gridColor}); Edit3DViewConfig::saveColors(DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, {gridColor}); - if (syncBackgroundColorAction->isChecked()) { - emitView3DAction(View3DActionType::SyncBackgroundColor, false); - syncBackgroundColorAction->setChecked(false); + if (syncEnvBackgroundAction->isChecked()) { + emitView3DAction(View3DActionType::SyncEnvBackground, false); + syncEnvBackgroundAction->setChecked(false); } }; @@ -545,17 +547,17 @@ void Edit3DView::createResetColorAction(QAction *syncBackgroundColorAction) tooltip); } -void Edit3DView::createSyncBackgroundColorAction() +void Edit3DView::createSyncEnvBackgroundAction() { - QString description = QCoreApplication::translate("SyncEdit3DColorAction", - "Use Scene Environment Color"); - QString tooltip = QCoreApplication::translate("SyncEdit3DColorAction", + QString description = QCoreApplication::translate("SyncEnvBackgroundAction", + "Use Scene Environment"); + QString tooltip = QCoreApplication::translate("SyncEnvBackgroundAction", "Sets the 3D view to use the Scene Environment " - "color as background color."); + "color or skybox as background color."); - m_syncBackgroundColorAction = std::make_unique( - QmlDesigner::Constants::EDIT3D_EDIT_SYNC_BACKGROUND_COLOR, - View3DActionType::SyncBackgroundColor, + m_syncEnvBackgroundAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_EDIT_SYNC_ENV_BACKGROUND, + View3DActionType::SyncEnvBackground, description, QKeySequence(), true, @@ -1026,14 +1028,14 @@ void Edit3DView::createEdit3DActions() m_visibilityToggleActions << m_showCameraFrustumAction.get(); m_visibilityToggleActions << m_showParticleEmitterAction.get(); - createSyncBackgroundColorAction(); - createSelectBackgroundColorAction(m_syncBackgroundColorAction->action()); + createSyncEnvBackgroundAction(); + createSelectBackgroundColorAction(m_syncEnvBackgroundAction->action()); createGridColorSelectionAction(); - createResetColorAction(m_syncBackgroundColorAction->action()); + createResetColorAction(m_syncEnvBackgroundAction->action()); m_backgroundColorActions << m_selectBackgroundColorAction.get(); m_backgroundColorActions << m_selectGridColorAction.get(); - m_backgroundColorActions << m_syncBackgroundColorAction.get(); + m_backgroundColorActions << m_syncEnvBackgroundAction.get(); m_backgroundColorActions << m_resetColorAction.get(); } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 2b2bd57d93f..2fb1e0451cd 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -101,10 +101,10 @@ private: void showMaterialPropertiesView(); void updateAlignActionStates(); - void createSelectBackgroundColorAction(QAction *syncBackgroundColorAction); + void createSelectBackgroundColorAction(QAction *syncEnvBackgroundAction); void createGridColorSelectionAction(); - void createResetColorAction(QAction *syncBackgroundColorAction); - void createSyncBackgroundColorAction(); + void createResetColorAction(QAction *syncEnvBackgroundAction); + void createSyncEnvBackgroundAction(); void createSeekerSliderAction(); QPoint resolveToolbarPopupPos(Edit3DAction *action) const; @@ -135,7 +135,7 @@ private: std::unique_ptr m_particlesPlayAction; std::unique_ptr m_particlesRestartAction; std::unique_ptr m_seekerAction; - std::unique_ptr m_syncBackgroundColorAction; + std::unique_ptr m_syncEnvBackgroundAction; std::unique_ptr m_selectBackgroundColorAction; std::unique_ptr m_selectGridColorAction; std::unique_ptr m_resetColorAction; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index ec29c3726a9..4ca8b1ea923 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -52,7 +52,7 @@ const char EDIT3D_EDIT_SHOW_GRID[] = "QmlDesigner.Editor3D.ToggleGrid"; const char EDIT3D_EDIT_SELECT_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.SelectBackgroundColor"; const char EDIT3D_EDIT_SELECT_GRID_COLOR[] = "QmlDesigner.Editor3D.SelectGridColor"; const char EDIT3D_EDIT_RESET_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.ResetBackgroundColor"; -const char EDIT3D_EDIT_SYNC_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.SyncBackgroundColor"; +const char EDIT3D_EDIT_SYNC_ENV_BACKGROUND[] = "QmlDesigner.Editor3D.SyncEnvBackground"; const char EDIT3D_EDIT_SHOW_SELECTION_BOX[] = "QmlDesigner.Editor3D.ToggleSelectionBox"; const char EDIT3D_EDIT_SHOW_ICON_GIZMO[] = "QmlDesigner.Editor3D.ToggleIconGizmo"; const char EDIT3D_EDIT_SHOW_CAMERA_FRUSTUM[] = "QmlDesigner.Editor3D.ToggleCameraFrustum"; diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 71c5e4d81e7..a44a2a75d2d 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -27,7 +27,7 @@ Item { property color backgroundGradientColorStart: "#222222" property color backgroundGradientColorEnd: "#999999" property color gridColor: "#cccccc" - property bool syncBackgroundColor: false + property bool syncEnvBackground: false enum SelectionMode { Item, Group } enum TransformMode { Move, Rotate, Scale } @@ -58,7 +58,7 @@ Item { onShowEditLightChanged: _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight) onGlobalOrientationChanged: _generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation) onShowGridChanged: _generalHelper.storeToolState(sceneId, "showGrid", showGrid); - onSyncBackgroundColorChanged: _generalHelper.storeToolState(sceneId, "syncBackgroundColor", syncBackgroundColor); + onSyncEnvBackgroundChanged: _generalHelper.storeToolState(sceneId, "syncEnvBackground", syncEnvBackground); onShowSelectionBoxChanged: _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox); onShowIconGizmoChanged: _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo); onShowCameraFrustumChanged: _generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum); @@ -136,10 +136,7 @@ Item { } } - if (syncBackgroundColor) - updateBackgroundColors([_generalHelper.sceneEnvironmentColor(sceneId)]); - else - updateBackgroundColors(_generalHelper.bgColor); + updateEnvBackground(); notifyActiveSceneChange(); } @@ -206,6 +203,31 @@ Item { } } + function updateEnvBackground() { + updateBackgroundColors(_generalHelper.bgColor); + + if (!editView) + return; + + if (syncEnvBackground) { + let bgMode = _generalHelper.sceneEnvironmentBgMode(sceneId); + if ((!_generalHelper.sceneEnvironmentLightProbe(sceneId) && bgMode === SceneEnvironment.SkyBox) + || (!_generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId) && bgMode === SceneEnvironment.SkyBoxCubeMap)) { + editView.sceneEnv.backgroundMode = SceneEnvironment.Color; + } else { + editView.sceneEnv.backgroundMode = bgMode; + } + editView.sceneEnv.lightProbe = _generalHelper.sceneEnvironmentLightProbe(sceneId); + editView.sceneEnv.skyBoxCubeMap = _generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId); + editView.sceneEnv.clearColor = _generalHelper.sceneEnvironmentColor(sceneId); + } else { + editView.sceneEnv.backgroundMode = SceneEnvironment.Transparent; + editView.sceneEnv.lightProbe = null; + editView.sceneEnv.skyBoxCubeMap = null; + editView.sceneEnv.clearColor = "transparent"; + } + } + // If resetToDefault is true, tool states not specifically set to anything will be reset to // their default state. function updateToolStates(toolStates, resetToDefault) @@ -220,15 +242,12 @@ Item { else if (resetToDefault) showGrid = true; - if ("syncBackgroundColor" in toolStates) { - syncBackgroundColor = toolStates.syncBackgroundColor; - if (syncBackgroundColor) - updateBackgroundColors([_generalHelper.sceneEnvironmentColor(sceneId)]); - else - updateBackgroundColors(_generalHelper.bgColor); + if ("syncEnvBackground" in toolStates) { + syncEnvBackground = toolStates.syncEnvBackground; + updateEnvBackground(); } else if (resetToDefault) { - syncBackgroundColor = false; - updateBackgroundColors(_generalHelper.bgColor); + syncEnvBackground = false; + updateEnvBackground(); } if ("showSelectionBox" in toolStates) @@ -281,7 +300,7 @@ Item { { _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight) _generalHelper.storeToolState(sceneId, "showGrid", showGrid) - _generalHelper.storeToolState(sceneId, "syncBackgroundColor", syncBackgroundColor) + _generalHelper.storeToolState(sceneId, "syncEnvBackground", syncEnvBackground) _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox) _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo) _generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum) @@ -697,6 +716,7 @@ Item { } } } + function onHiddenStateChanged(node) { for (var i = 0; i < cameraGizmos.length; ++i) { @@ -727,11 +747,16 @@ Item { } } } + function onUpdateDragTooltip() { gizmoLabel.updateLabel(); rotateGizmoLabel.updateLabel(); } + + function onSceneEnvDataChanged() { + updateEnvBackground(); + } } Node { diff --git a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml index e3f585de545..0eafeeb816e 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml @@ -15,6 +15,7 @@ View3D { property alias sceneHelpers: sceneHelpers property alias perspectiveCamera: scenePerspectiveCamera property alias orthoCamera: sceneOrthoCamera + property alias sceneEnv: sceneEnv property vector3d cameraLookAt // Measuring the distance from camera to lookAt plus the distance of lookAt from grid plane diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 86267874110..6f419fdf410 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -57,6 +57,10 @@ GeneralHelper::GeneralHelper() m_toolStateUpdateTimer.setSingleShot(true); QObject::connect(&m_toolStateUpdateTimer, &QTimer::timeout, this, &GeneralHelper::handlePendingToolStateUpdate); + + QList defaultBg; + defaultBg.append(QColor()); + m_bgColor = QVariant::fromValue(defaultBg); } void GeneralHelper::requestOverlayUpdate() @@ -540,19 +544,62 @@ void GeneralHelper::storeToolState(const QString &sceneId, const QString &tool, } } -void GeneralHelper::setSceneEnvironmentColor(const QString &sceneId, const QColor &color) +void GeneralHelper::setSceneEnvironmentData(const QString &sceneId, + QQuick3DSceneEnvironment *env) { - m_sceneEnvironmentColor[sceneId] = color; + if (env) { + SceneEnvData &data = m_sceneEnvironmentData[sceneId]; + data.backgroundMode = env->backgroundMode(); + data.clearColor = env->clearColor(); + + if (data.lightProbe) + disconnect(data.lightProbe, &QObject::destroyed, this, &GeneralHelper::sceneEnvDataChanged); + data.lightProbe = env->lightProbe(); + if (env->lightProbe()) + connect(env->lightProbe(), &QObject::destroyed, this, &GeneralHelper::sceneEnvDataChanged, Qt::DirectConnection); + + if (data.skyBoxCubeMap) + disconnect(data.skyBoxCubeMap, &QObject::destroyed, this, &GeneralHelper::sceneEnvDataChanged); + data.skyBoxCubeMap = env->skyBoxCubeMap(); + if (env->skyBoxCubeMap()) + connect(env->skyBoxCubeMap(), &QObject::destroyed, this, &GeneralHelper::sceneEnvDataChanged, Qt::DirectConnection); + + emit sceneEnvDataChanged(); + } +} + +QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes GeneralHelper::sceneEnvironmentBgMode( + const QString &sceneId) const +{ + return m_sceneEnvironmentData[sceneId].backgroundMode; } QColor GeneralHelper::sceneEnvironmentColor(const QString &sceneId) const { - return m_sceneEnvironmentColor[sceneId]; + return m_sceneEnvironmentData[sceneId].clearColor; } -void GeneralHelper::clearSceneEnvironmentColors() +QQuick3DTexture *GeneralHelper::sceneEnvironmentLightProbe(const QString &sceneId) const { - m_sceneEnvironmentColor.clear(); + return m_sceneEnvironmentData[sceneId].lightProbe.data(); +} + +QQuick3DCubeMapTexture *GeneralHelper::sceneEnvironmentSkyBoxCubeMap(const QString &sceneId) const +{ + return m_sceneEnvironmentData[sceneId].skyBoxCubeMap.data(); +} + +void GeneralHelper::clearSceneEnvironmentData() +{ + for (const SceneEnvData &data : std::as_const(m_sceneEnvironmentData)) { + if (data.lightProbe) + disconnect(data.lightProbe, &QObject::destroyed, this, &GeneralHelper::sceneEnvDataChanged); + if (data.skyBoxCubeMap) + disconnect(data.skyBoxCubeMap, &QObject::destroyed, this, &GeneralHelper::sceneEnvDataChanged); + } + + m_sceneEnvironmentData.clear(); + emit sceneEnvDataChanged(); } void GeneralHelper::initToolStates(const QString &sceneId, const QVariantMap &toolStates) diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index e44c2c60ce9..9e54250201a 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -15,7 +15,10 @@ #include #include #include +#include #include +#include +#include QT_BEGIN_NAMESPACE class QQuick3DCamera; @@ -92,9 +95,13 @@ public: Q_INVOKABLE void scaleMultiSelection(bool commit); Q_INVOKABLE void rotateMultiSelection(bool commit); - void setSceneEnvironmentColor(const QString &sceneId, const QColor &color); + void setSceneEnvironmentData(const QString &sceneId, QQuick3DSceneEnvironment *env); + Q_INVOKABLE QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes sceneEnvironmentBgMode( + const QString &sceneId) const; Q_INVOKABLE QColor sceneEnvironmentColor(const QString &sceneId) const; - void clearSceneEnvironmentColors(); + Q_INVOKABLE QQuick3DTexture *sceneEnvironmentLightProbe(const QString &sceneId) const; + Q_INVOKABLE QQuick3DCubeMapTexture *sceneEnvironmentSkyBoxCubeMap(const QString &sceneId) const; + void clearSceneEnvironmentData(); bool isMacOS() const; @@ -137,6 +144,7 @@ signals: void bgColorChanged(); void minGridStepChanged(); void updateDragTooltip(); + void sceneEnvDataChanged(); private: void handlePendingToolStateUpdate(); @@ -150,9 +158,16 @@ private: QTimer m_toolStateUpdateTimer; QHash m_toolStates; QHash m_toolStatesPending; - QHash m_sceneEnvironmentColor; QSet m_rotationBlockedNodes; + struct SceneEnvData { + QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes backgroundMode; + QColor clearColor; + QPointer lightProbe; + QPointer skyBoxCubeMap; + }; + QHash m_sceneEnvironmentData; + struct MultiSelData { QVector3D startScenePos; QVector3D startScale; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index ea052aa426c..d05de5635b0 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -385,10 +385,9 @@ void Qt5InformationNodeInstanceServer::updateColorSettings( QQmlProperty gridProp(m_editView3DData.rootItem, "gridColor", context()); gridProp.write(container.value()); } else if (container.name() == "edit3dBgColor") { - QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateBackgroundColors", - Q_ARG(QVariant, container.value())); if (auto helper = qobject_cast(m_3dHelper)) helper->setBgColor(container.value()); + QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateEnvBackground"); } } } @@ -1066,7 +1065,7 @@ void Qt5InformationNodeInstanceServer::resolveSceneRoots() ++it; } - updateSceneEnvColorsToHelper(); + updateSceneEnvToHelper(); if (updateActiveScene) { m_active3DView = findView3DForSceneRoot(m_active3DScene); @@ -1912,7 +1911,7 @@ void Qt5InformationNodeInstanceServer::setup3DEditView( m_editView3DSetupDone = true; - updateSceneEnvColorsToHelper(); + updateSceneEnvToHelper(); if (toolStates.contains({})) { // Update tool state to an existing no-scene state before updating the active scene to @@ -2232,15 +2231,15 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm render3DEditView(2); } -void Qt5InformationNodeInstanceServer::setSceneEnvironmentColor( - [[maybe_unused]] const PropertyValueContainer &container) +void Qt5InformationNodeInstanceServer::setSceneEnvironmentData( + [[maybe_unused]] qint32 instanceId) { #ifdef QUICK3D_MODULE auto helper = qobject_cast(m_3dHelper); - if (!helper || !hasInstanceForId(container.instanceId()) || !m_active3DView) + if (!helper || !hasInstanceForId(instanceId) || !m_active3DView) return; - ServerNodeInstance sceneEnvInstance = instanceForId(container.instanceId()); + ServerNodeInstance sceneEnvInstance = instanceForId(instanceId); if (!sceneEnvInstance.isSubclassOf("QQuick3DSceneEnvironment")) return; @@ -2255,17 +2254,14 @@ void Qt5InformationNodeInstanceServer::setSceneEnvironmentColor( ServerNodeInstance activeSceneInstance = active3DSceneInstance(); const QString sceneId = activeSceneInstance.id(); - QColor color = container.value().value(); - helper->setSceneEnvironmentColor(sceneId, color); + helper->setSceneEnvironmentData(sceneId, activeEnv); + QVariantMap toolStates = helper->getToolStates(sceneId); - if (toolStates.contains("syncBackgroundColor")) { - bool sync = toolStates["syncBackgroundColor"].toBool(); - if (sync) { - QList colors{color}; - QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateBackgroundColors", - Q_ARG(QVariant, QVariant::fromValue(colors))); - } + if (toolStates.contains("syncEnvBackground")) { + bool sync = toolStates["syncEnvBackground"].toBool(); + if (sync) + QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateEnvBackground"); } #endif } @@ -2308,15 +2304,15 @@ QVariantList Qt5InformationNodeInstanceServer::alignCameraList() const return cameras; } -void Qt5InformationNodeInstanceServer::updateSceneEnvColorsToHelper() +void Qt5InformationNodeInstanceServer::updateSceneEnvToHelper() { #ifdef QUICK3D_MODULE - // Update stored scene environment colors for all scenes + // Update stored scene environment backgrounds for all scenes auto helper = qobject_cast(m_3dHelper); if (!helper) return; - helper->clearSceneEnvironmentColors(); + helper->clearSceneEnvironmentData(); const auto sceneRoots = m_3DSceneMap.uniqueKeys(); for (QObject *sceneRoot : sceneRoots) { @@ -2328,32 +2324,36 @@ void Qt5InformationNodeInstanceServer::updateSceneEnvColorsToHelper() if (!env) continue; - QColor clearColor = env->clearColor(); - if (clearColor.isValid() && helper) { - ServerNodeInstance sceneInstance; - if (hasInstanceForObject(sceneRoot)) - sceneInstance = instanceForObject(sceneRoot); - else if (hasInstanceForObject(view3D)) - sceneInstance = instanceForObject(view3D); + ServerNodeInstance sceneInstance; + if (hasInstanceForObject(sceneRoot)) + sceneInstance = instanceForObject(sceneRoot); + else if (hasInstanceForObject(view3D)) + sceneInstance = instanceForObject(view3D); - const QString sceneId = sceneInstance.id(); + const QString sceneId = sceneInstance.id(); - helper->setSceneEnvironmentColor(sceneId, clearColor); - } + helper->setSceneEnvironmentData(sceneId, env); } #endif } +bool Qt5InformationNodeInstanceServer::isSceneEnvironmentBgProperty(const PropertyName &name) const +{ + return name == "backgroundMode" || name == "clearColor" + || name == "lightProbe" || name == "skyBoxCubeMap"; +} + void Qt5InformationNodeInstanceServer::changePropertyValues(const ChangeValuesCommand &command) { bool hasDynamicProperties = false; const QVector values = command.valueChanges(); + QSet sceneEnvs; for (const PropertyValueContainer &container : values) { if (!container.isReflected()) { hasDynamicProperties |= container.isDynamic(); - if (container.name() == "clearColor") - setSceneEnvironmentColor(container); + if (isSceneEnvironmentBgProperty(container.name())) + sceneEnvs.insert(container.instanceId()); setInstancePropertyVariant(container); } @@ -2362,6 +2362,9 @@ void Qt5InformationNodeInstanceServer::changePropertyValues(const ChangeValuesCo if (hasDynamicProperties) refreshBindings(); + for (const qint32 id : std::as_const(sceneEnvs)) + setSceneEnvironmentData(id); + startRenderTimer(); render3DEditView(); @@ -2455,8 +2458,8 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c case View3DActionType::ShowCameraFrustum: updatedToolState.insert("showCameraFrustum", command.isEnabled()); break; - case View3DActionType::SyncBackgroundColor: - updatedToolState.insert("syncBackgroundColor", command.isEnabled()); + case View3DActionType::SyncEnvBackground: + updatedToolState.insert("syncEnvBackground", command.isEnabled()); break; #ifdef QUICK3D_PARTICLES_MODULE case View3DActionType::ShowParticleEmitter: @@ -2530,6 +2533,16 @@ void Qt5InformationNodeInstanceServer::changeAuxiliaryValues(const ChangeAuxilia void Qt5InformationNodeInstanceServer::changePropertyBindings(const ChangeBindingsCommand &command) { Qt5NodeInstanceServer::changePropertyBindings(command); + + QSet sceneEnvs; + for (const PropertyBindingContainer &container : std::as_const(command.bindingChanges)) { + if (isSceneEnvironmentBgProperty(container.name())) + sceneEnvs.insert(container.instanceId()); + } + + for (const qint32 id : std::as_const(sceneEnvs)) + setSceneEnvironmentData(id); + render3DEditView(); } @@ -2570,15 +2583,18 @@ void Qt5InformationNodeInstanceServer::changeState(const ChangeStateCommand &com void Qt5InformationNodeInstanceServer::removeProperties(const RemovePropertiesCommand &command) { const QVector props = command.properties(); + QSet sceneEnvs; + for (const PropertyAbstractContainer &container : props) { - if (container.name() == "clearColor") { - setSceneEnvironmentColor(PropertyValueContainer(container.instanceId(), - container.name(), {}, {})); - } + if (isSceneEnvironmentBgProperty(container.name())) + sceneEnvs.insert(container.instanceId()); } Qt5NodeInstanceServer::removeProperties(command); + for (const qint32 id : std::as_const(sceneEnvs)) + setSceneEnvironmentData(id); + render3DEditView(); } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index c774bc8f33f..3ac044cbace 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -136,9 +136,10 @@ private: void resetParticleSystem(); void handleParticleSystemDeselected(); #endif - void setSceneEnvironmentColor(const PropertyValueContainer &container); + void setSceneEnvironmentData(qint32 instanceId); QVariantList alignCameraList() const; - void updateSceneEnvColorsToHelper(); + void updateSceneEnvToHelper(); + bool isSceneEnvironmentBgProperty(const PropertyName &name) const; RenderViewData m_editView3DData; RenderViewData m_modelNode3DImageViewData; From 8c4f3d4970eb0fd881729ac8ac7a99ccf4c3de80 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 14 Sep 2023 13:06:43 +0200 Subject: [PATCH 110/130] QmlDesigner: Fix missing updatedProjectSourceIds Otherwise we get constraint errors in the project storage later because we try to insert a entry instead of updating it. Change-Id: I25941a30c6d35e189153e49d5092e85ae0c7e683 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../projectstorage/projectstorageupdater.cpp | 4 +- .../projectstorageupdater-test.cpp | 46 +++++++++---------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 624b4c47c9c..3759736cfaf 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -227,8 +227,10 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, package, notUpdatedSourceIds); - if (state == FileState::Changed) + if (state == FileState::Changed) { package.projectDatas.push_back(std::move(projectData)); + package.updatedProjectSourceIds.push_back(sourceId); + } } } diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index 2b7c4b83648..c167e3eb8b5 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -1164,28 +1164,27 @@ TEST_F(ProjectStorageUpdater, update_qml_types_files_is_empty) TEST_F(ProjectStorageUpdater, update_qml_types_files) { EXPECT_CALL(projectStorageMock, - synchronize( - AllOf(Field(&SynchronizationPackage::imports, - UnorderedElementsAre(import4, import5)), - Field(&SynchronizationPackage::types, - UnorderedElementsAre(objectType, itemType)), - Field(&SynchronizationPackage::updatedSourceIds, - UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::fileStatuses, - UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 1, 21), - IsFileStatus(qmltypes2PathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedFileStatusSourceIds, - UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(qmltypesPathSourceId, - qmltypesPathSourceId, - builtinCppNativeModuleId, - FileType::QmlTypes), - IsProjectData(qmltypes2PathSourceId, - qmltypes2PathSourceId, - builtinCppNativeModuleId, - FileType::QmlTypes))), - Field(&SynchronizationPackage::updatedProjectSourceIds, IsEmpty())))); + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import4, import5)), + Field(&SynchronizationPackage::types, UnorderedElementsAre(objectType, itemType)), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 1, 21), + IsFileStatus(qmltypes2PathSourceId, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), + Field(&SynchronizationPackage::projectDatas, + UnorderedElementsAre(IsProjectData(qmltypesPathSourceId, + qmltypesPathSourceId, + builtinCppNativeModuleId, + FileType::QmlTypes), + IsProjectData(qmltypes2PathSourceId, + qmltypes2PathSourceId, + builtinCppNativeModuleId, + FileType::QmlTypes))), + Field(&SynchronizationPackage::updatedProjectSourceIds, + UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}); } @@ -1209,7 +1208,8 @@ TEST_F(ProjectStorageUpdater, dont_update_qml_types_files_if_unchanged) qmltypesPathSourceId, builtinCppNativeModuleId, FileType::QmlTypes))), - Field(&SynchronizationPackage::updatedProjectSourceIds, IsEmpty())))); + Field(&SynchronizationPackage::updatedProjectSourceIds, + UnorderedElementsAre(qmltypesPathSourceId))))); updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}); } From f9956242040f37e53a5057dc4236e86e6107c76b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 20 Sep 2023 08:49:09 +0200 Subject: [PATCH 111/130] QmlDesigner: Remove studio plugin The plugin is only used for a configuration file. It is the only place which provides that configuration. So we remove all that code and hard code that list. Change-Id: Idb58772c9fa7ffaa955e49842fe55a25a9a154ed Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 2 +- .../itemlibrary/itemlibraryaddimportmodel.cpp | 30 ++++---- .../itemlibrary/itemlibraryaddimportmodel.h | 1 - .../itemlibrary/itemlibraryconstants.h | 18 +++++ .../itemlibrary/itemlibrarywidget.cpp | 14 +--- .../itemlibrary/itemlibrarywidget.h | 1 - .../designercore/include/itemlibraryinfo.h | 10 --- .../designercore/include/metainforeader.h | 1 - .../designercore/metainfo/itemlibraryinfo.cpp | 29 -------- .../designercore/metainfo/metainforeader.cpp | 21 +----- .../designercore/model/texttomodelmerger.cpp | 69 +++++++++++++------ .../qmldesigner/studioplugin/CMakeLists.txt | 10 --- .../qmldesigner/studioplugin/studioplugin.cpp | 22 ------ .../qmldesigner/studioplugin/studioplugin.h | 29 -------- .../studioplugin/studioplugin.json | 6 -- .../studioplugin/studioplugin.metainfo | 61 ---------------- .../qmldesigner/studioplugin/studioplugin.qrc | 5 -- 17 files changed, 87 insertions(+), 242 deletions(-) create mode 100644 src/plugins/qmldesigner/components/itemlibrary/itemlibraryconstants.h delete mode 100644 src/plugins/qmldesigner/studioplugin/CMakeLists.txt delete mode 100644 src/plugins/qmldesigner/studioplugin/studioplugin.cpp delete mode 100644 src/plugins/qmldesigner/studioplugin/studioplugin.h delete mode 100644 src/plugins/qmldesigner/studioplugin/studioplugin.json delete mode 100644 src/plugins/qmldesigner/studioplugin/studioplugin.metainfo delete mode 100644 src/plugins/qmldesigner/studioplugin/studioplugin.qrc diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 78810346650..8ea79bcdcf8 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -683,6 +683,7 @@ extend_qtc_plugin(QmlDesigner assetimportupdatetreemodel.cpp assetimportupdatetreemodel.h assetimportupdatetreeview.cpp assetimportupdatetreeview.h itemlibrary.qrc + itemlibraryconstants.h itemlibraryimageprovider.cpp itemlibraryimageprovider.h itemlibraryitem.cpp itemlibraryitem.h itemlibrarymodel.cpp itemlibrarymodel.h @@ -1199,4 +1200,3 @@ extend_qtc_plugin(qtquickplugin qtquickplugin.cpp qtquickplugin.h qtquickplugin.qrc ) -add_subdirectory(studioplugin) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp index 66afa19f8b8..8b3f2dee79d 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "itemlibraryaddimportmodel.h" +#include "itemlibraryconstants.h" #include #include @@ -56,6 +57,14 @@ QHash ItemLibraryAddImportModel::roleNames() const return m_roleNames; } +namespace { +bool isPriorityImport(QStringView importUrl) +{ + return std::find(std::begin(priorityImports), std::end(priorityImports), importUrl) + != std::end(priorityImports); +} +} // namespace + void ItemLibraryAddImportModel::update(const Imports &possibleImports) { beginResetModel(); @@ -77,7 +86,7 @@ void ItemLibraryAddImportModel::update(const Imports &possibleImports) filteredImports = possibleImports; } - Utils::sort(filteredImports, [this](const Import &firstImport, const Import &secondImport) { + Utils::sort(filteredImports, [](const Import &firstImport, const Import &secondImport) { if (firstImport.url() == secondImport.url()) return firstImport.toString() < secondImport.toString(); @@ -87,8 +96,8 @@ void ItemLibraryAddImportModel::update(const Imports &possibleImports) if (secondImport.url() == "QtQuick") return false; - const bool firstPriority = m_priorityImports.contains(firstImport.url()); - if (firstPriority != m_priorityImports.contains(secondImport.url())) + const bool firstPriority = isPriorityImport(firstImport.url()); + if (firstPriority != isPriorityImport(secondImport.url())) return firstPriority; if (firstImport.isLibraryImport() && secondImport.isFileImport()) @@ -109,11 +118,11 @@ void ItemLibraryAddImportModel::update(const Imports &possibleImports) // create import sections bool previousIsPriority = false; for (const Import &import : std::as_const(filteredImports)) { - bool currentIsPriority = m_priorityImports.contains(import.url()); - if (previousIsPriority && !currentIsPriority) - m_importList.append(Import::empty()); // empty import acts as a separator - m_importList.append(import); - previousIsPriority = currentIsPriority; + bool currentIsPriority = isPriorityImport(import.url()); + if (previousIsPriority && !currentIsPriority) + m_importList.append(Import::empty()); // empty import acts as a separator + m_importList.append(import); + previousIsPriority = currentIsPriority; } endResetModel(); @@ -151,9 +160,4 @@ Import ItemLibraryAddImportModel::getImportAt(int index) const return m_importList.at(index); } -void ItemLibraryAddImportModel::setPriorityImports(const QSet &priorityImports) -{ - m_priorityImports = priorityImports; -} - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.h index 2989fd51642..c70ef9f4046 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.h @@ -28,7 +28,6 @@ public: void setSearchText(const QString &searchText); Import getImportAt(int index) const; - void setPriorityImports(const QSet &priorityImports); Import getImport(const QString &importUrl) const; private: diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryconstants.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryconstants.h new file mode 100644 index 00000000000..139d3fe3380 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryconstants.h @@ -0,0 +1,18 @@ +// 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 + +namespace QmlDesigner { + +inline constexpr QStringView priorityImports[] = {u"QtQuick.Controls", + u"QtQuick.Layouts", + u"QtQuick.Studio.Effects", + u"QtQuick.Studio.Components", + u"QtQuick.Studio.MultiText", + u"QtQuick.Studio.LogicHelper", + u"Qt.SafeRenderer", + u"QtQuick3D", + u"FlowView"}; // have to be sorted + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index d2435fbf15a..8d3603ea841 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -3,6 +3,7 @@ #include "itemlibrarywidget.h" +#include "itemlibraryconstants.h" #include "itemlibraryiconimageprovider.h" #include "itemlibraryimport.h" @@ -183,16 +184,11 @@ void ItemLibraryWidget::setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo) if (m_itemLibraryInfo) { disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged, this, &ItemLibraryWidget::delayedUpdateModel); - disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::priorityImportsChanged, - this, &ItemLibraryWidget::handlePriorityImportsChanged); } m_itemLibraryInfo = itemLibraryInfo; if (itemLibraryInfo) { connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged, this, &ItemLibraryWidget::delayedUpdateModel); - connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::priorityImportsChanged, - this, &ItemLibraryWidget::handlePriorityImportsChanged); - m_addModuleModel->setPriorityImports(m_itemLibraryInfo->priorityImports()); } delayedUpdateModel(); } @@ -354,14 +350,6 @@ void ItemLibraryWidget::updateSearch() m_addModuleModel->setSearchText(m_filterText); } -void ItemLibraryWidget::handlePriorityImportsChanged() -{ - if (!m_itemLibraryInfo.isNull()) { - m_addModuleModel->setPriorityImports(m_itemLibraryInfo->priorityImports()); - m_addModuleModel->update(set_difference(m_model->possibleImports(), m_model->imports())); - } -} - void ItemLibraryWidget::setIsDragging(bool val) { if (m_isDragging != val) { diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index 38cd782d097..e791fceb698 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -90,7 +90,6 @@ private: void reloadQmlSource(); void updateSearch(); - void handlePriorityImportsChanged(); void setIsDragging(bool val); static QString getDependencyImport(const Import &import); diff --git a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h index 034af72410f..2974d58d0b5 100644 --- a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h +++ b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h @@ -88,15 +88,8 @@ public: bool containsEntry(const ItemLibraryEntry &entry); void clearEntries(); - QStringList blacklistImports() const; - QSet priorityImports() const; - - void addBlacklistImports(const QStringList &list); - void addPriorityImports(const QSet &set); - signals: void entriesChanged(); - void priorityImportsChanged(); private: // functions ItemLibraryInfo(QObject *parent = nullptr); @@ -105,9 +98,6 @@ private: // functions private: // variables QHash m_nameToEntryHash; QPointer m_baseInfo; - - QStringList m_blacklistImports; - QSet m_priorityImports; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/metainforeader.h b/src/plugins/qmldesigner/designercore/include/metainforeader.h index 4b4a3837e34..b10c7413c59 100644 --- a/src/plugins/qmldesigner/designercore/include/metainforeader.h +++ b/src/plugins/qmldesigner/designercore/include/metainforeader.h @@ -65,7 +65,6 @@ private: ParserSate readExtraFileElement(const QString &name); void readTypeProperty(const QString &name, const QVariant &value); - void readImportsProperty(const QString &name, const QVariant &value); void readItemLibraryEntryProperty(const QString &name, const QVariant &value); void readPropertyProperty(const QString &name, const QVariant &value); void readQmlSourceProperty(const QString &name, const QVariant &value); diff --git a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp index cbde96e9926..9f542826156 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp @@ -334,35 +334,6 @@ void ItemLibraryInfo::clearEntries() emit entriesChanged(); } -QStringList ItemLibraryInfo::blacklistImports() const -{ - auto list = m_blacklistImports; - if (m_baseInfo) - list.append(m_baseInfo->m_blacklistImports); - return list; -} - -QSet ItemLibraryInfo::priorityImports() const -{ - QSet set = m_priorityImports; - if (m_baseInfo) - set.unite(m_baseInfo->m_priorityImports); - return set; -} - -void ItemLibraryInfo::addBlacklistImports(const QStringList &list) -{ - m_blacklistImports.append(list); -} - -void ItemLibraryInfo::addPriorityImports(const QSet &set) -{ - if (!set.isEmpty()) { - m_priorityImports.unite(set); - emit priorityImportsChanged(); - } -} - void ItemLibraryInfo::setBaseInfo(ItemLibraryInfo *baseInfo) { m_baseInfo = baseInfo; diff --git a/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp b/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp index a56221f6438..af61ef65735 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp @@ -116,7 +116,8 @@ void MetaInfoReader::propertyDefinition(const QString &name, { switch (parserState()) { case ParsingType: readTypeProperty(name, value); break; - case ParsingImports: readImportsProperty(name, value); break; + case ParsingImports: + break; // not supported anymore case ParsingItemLibrary: readItemLibraryEntryProperty(name, value); break; case ParsingProperty: readPropertyProperty(name, value); break; case ParsingQmlSource: readQmlSourceProperty(name, value); break; @@ -220,24 +221,6 @@ MetaInfoReader::ParserSate MetaInfoReader::readExtraFileElement(const QString &n return Error; } -void MetaInfoReader::readImportsProperty(const QString &name, const QVariant &value) -{ - const auto values = value.toStringList(); - - if (name == "blacklistImports" && !values.isEmpty()) { - m_metaInfo.itemLibraryInfo()->addBlacklistImports(values); - } else if ((name == "priorityImports" || name == "showTagsForImports") && !values.isEmpty()) { - // Flow tags are no longer shown, but the old property is still supported for prioritizing - // imports to keep compatibility with old metainfo files. - m_metaInfo.itemLibraryInfo()->addPriorityImports(Utils::toSet(values)); - } else { - addError(::QmlDesigner::Internal::MetaInfoReader::tr("Unknown property for Imports %1") - .arg(name), - currentSourceLocation()); - setParserState(Error); - } -} - void MetaInfoReader::readTypeProperty(const QString &name, const QVariant &value) { if (name == QLatin1String("name")) { diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index dd4fe3b2555..aed4a78d1d2 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -19,6 +19,7 @@ #include "rewriterview.h" #include "signalhandlerproperty.h" #include "variantproperty.h" + #include #include #include @@ -828,15 +829,6 @@ void TextToModelMerger::setupImports(const Document::Ptr &doc, namespace { -bool skipByMetaInfo(QStringView moduleName, const QStringList &skipModuleNames) -{ - return std::any_of(skipModuleNames.begin(), - skipModuleNames.end(), - [&](const QString &skipModuleName) { - return moduleName.contains(skipModuleName); - }); -} - class StartsWith : public QStringView { public: @@ -876,7 +868,7 @@ constexpr auto skipModules = std::make_tuple(EndsWith(u".impl"), EndsWith(u".private"), EndsWith(u".Private"), Equals(u"QtQuick.Particles"), - Equals(u"QtQuick.Dialogs"), + StartsWith(u"QtQuick.Dialogs"), Equals(u"QtQuick.Controls.Styles"), Equals(u"QtNfc"), Equals(u"Qt.WebSockets"), @@ -886,7 +878,51 @@ constexpr auto skipModules = std::make_tuple(EndsWith(u".impl"), Equals(u"QtWinExtras"), Equals(u"QtPurchasing"), Equals(u"QtBluetooth"), - Equals(u"Enginio")); + Equals(u"Enginio"), + StartsWith(u"Qt.labs."), + StartsWith(u"Qt.test.controls"), + StartsWith(u"QmlTime"), + StartsWith(u"Qt.labs."), + StartsWith(u"Qt.test.controls"), + StartsWith(u"Qt3D."), + StartsWith(u"Qt5Compat.GraphicalEffects"), + StartsWith(u"QtCanvas3D"), + StartsWith(u"QtCore"), + StartsWith(u"QtDataVisualization"), + StartsWith(u"QtGamepad"), + StartsWith(u"QtOpcUa"), + StartsWith(u"QtPositioning"), + Equals(u"QtQuick.Controls.Basic"), + Equals(u"QtQuick.Controls.Fusion"), + Equals(u"QtQuick.Controls.Imagine"), + Equals(u"QtQuick.Controls.Material"), + Equals(u"QtQuick.Controls.NativeStyle"), + Equals(u"QtQuick.Controls.Universal"), + Equals(u"QtQuick.Controls.Windows"), + StartsWith(u"QtQuick.LocalStorage"), + StartsWith(u"QtQuick.NativeStyle"), + StartsWith(u"QtQuick.Pdf"), + StartsWith(u"QtQuick.Scene2D"), + StartsWith(u"QtQuick.Scene3D"), + StartsWith(u"QtQuick.Shapes"), + StartsWith(u"QtQuick.Studio.EventSimulator"), + StartsWith(u"QtQuick.Studio.EventSystem"), + StartsWith(u"QtQuick.Templates"), + StartsWith(u"QtQuick.VirtualKeyboard"), + StartsWith(u"QtQuick.tooling"), + StartsWith( + u"QtQuick3D MateriablacklistImportslEditor"), + StartsWith(u"QtQuick3D.ParticleEffects"), + StartsWith(u"QtRemoteObjects"), + StartsWith(u"QtRemoveObjects"), + StartsWith(u"QtScxml"), + StartsWith(u"QtSensors"), + StartsWith(u"QtTest"), + StartsWith(u"QtTextToSpeech"), + StartsWith(u"QtVncServer"), + StartsWith(u"QtWebEngine"), + StartsWith(u"QtWebSockets"), + StartsWith(u"QtWebView")); bool skipModule(QStringView moduleName) { @@ -894,11 +930,6 @@ bool skipModule(QStringView moduleName) skipModules); } -bool skipModule(QStringView moduleName, const QStringList &skipModuleNames) -{ - return skipModule(moduleName) || skipByMetaInfo(moduleName, skipModuleNames); -} - void collectPossibleFileImports(const QString &checkPath, QSet usedImportsSet, QList &possibleImports) @@ -975,12 +1006,8 @@ void TextToModelMerger::setupPossibleImports() auto &externalDependencies = m_rewriterView->externalDependencies(); if (externalDependencies.isQt6Project()) { - const auto skipModuleNames = m_rewriterView->model() - ->metaInfo() - .itemLibraryInfo() - ->blacklistImports(); ModuleScanner moduleScanner{[&](QStringView moduleName) { - return skipModule(moduleName, skipModuleNames); + return skipModule(moduleName); }, VersionScanning::No, m_rewriterView->externalDependencies()}; diff --git a/src/plugins/qmldesigner/studioplugin/CMakeLists.txt b/src/plugins/qmldesigner/studioplugin/CMakeLists.txt deleted file mode 100644 index 3c04dfd6243..00000000000 --- a/src/plugins/qmldesigner/studioplugin/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -add_qtc_plugin(StudioPlugin - PLUGIN_CLASS StudioPlugin - CONDITION TARGET QmlDesigner - DEPENDS Core QmlDesigner Utils ProjectExplorer - SOURCES - studioplugin.cpp - studioplugin.h - studioplugin.qrc - PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} -) diff --git a/src/plugins/qmldesigner/studioplugin/studioplugin.cpp b/src/plugins/qmldesigner/studioplugin/studioplugin.cpp deleted file mode 100644 index 9b4f67f36bc..00000000000 --- a/src/plugins/qmldesigner/studioplugin/studioplugin.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "studioplugin.h" - -namespace QmlDesigner { - -StudioPlugin::StudioPlugin() -{ -} - -QString StudioPlugin::pluginName() const -{ - return QLatin1String("StudioPlugin"); -} - -QString StudioPlugin::metaInfo() const -{ - return QLatin1String(":/studioplugin/studioplugin.metainfo"); -} - -} diff --git a/src/plugins/qmldesigner/studioplugin/studioplugin.h b/src/plugins/qmldesigner/studioplugin/studioplugin.h deleted file mode 100644 index 416b6df310a..00000000000 --- a/src/plugins/qmldesigner/studioplugin/studioplugin.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -QT_FORWARD_DECLARE_CLASS(QAction) - -namespace QmlDesigner { - -class StudioPlugin : public QObject, QmlDesigner::IWidgetPlugin -{ - Q_OBJECT - - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QmlDesignerPlugin" FILE "studioplugin.json") - - Q_DISABLE_COPY(StudioPlugin) - Q_INTERFACES(QmlDesigner::IWidgetPlugin) - -public: - StudioPlugin(); - ~StudioPlugin() override = default; - - QString metaInfo() const override; - QString pluginName() const override; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/studioplugin/studioplugin.json b/src/plugins/qmldesigner/studioplugin/studioplugin.json deleted file mode 100644 index 1370c2048dc..00000000000 --- a/src/plugins/qmldesigner/studioplugin/studioplugin.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Vendor" : "The Qt Company Ltd", - "Category" : "Qt Quick", - "Description" : "Plugin for Qt Studio", - "Url" : "http://www.qt.io" -} diff --git a/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo b/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo deleted file mode 100644 index 2998ea2f67b..00000000000 --- a/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo +++ /dev/null @@ -1,61 +0,0 @@ -MetaInfo { - - Imports { - blacklistImports: [ - "Qt.labs.", - "Qt3D.", - "QtCanvas3D", - "QtGamepad", - "QtPositioning", - "QtQuick.LocalStorage", - "QtQuick.Scene2D", - "QtQuick.Scene3D", - "QtQuick.VirtualKeyboard", - "QtScxml", - "QtTest", - "QtWebEngine", - "QtWebSockets", - "QtQuick.Dialogs.", - "QtSensors", - "QtRemoveObjects", - "QtQuick.tooling", - "QtWebView", - "QtCore", - "QtQuick.Controls.Basic", - "QtQuick.Controls.Fusion", - "QtQuick.Controls.Imagine", - "QtQuick.Controls.Universal", - "QtQuick.Controls.Material", - "QtQuick.Controls.NativeStyle", - "QtQuick.Controls.Windows", - "QtQuick.NativeStyle", - "QtRemoteObjects", - "Qt5Compat.GraphicalEffects", - "QtQuick.Templates", - "QtQuick.Shapes", - "QtQuick.Studio.EventSystem", - "QtQuick.Studio.EventSimulator", - "QtQuick.Pdf", - "QmlTime", - "Qt.test.controls", - "QtOpcUa", - "QtVncServer", - "QtTextToSpeech", - "QtQuick3D MaterialEditor", - "QtDataVisualization", - "QtQuick3D.ParticleEffects" - ] - - showTagsForImports: [ - "QtQuick.Controls", - "QtQuick.Layouts", - "QtQuick.Studio.Effects", - "QtQuick.Studio.Components", - "QtQuick.Studio.MultiText", - "QtQuick.Studio.LogicHelper", - "Qt.SafeRenderer", - "QtQuick3D", - "FlowView" - ] - } -} diff --git a/src/plugins/qmldesigner/studioplugin/studioplugin.qrc b/src/plugins/qmldesigner/studioplugin/studioplugin.qrc deleted file mode 100644 index 7196699789f..00000000000 --- a/src/plugins/qmldesigner/studioplugin/studioplugin.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - studioplugin.metainfo - - From 3a0eff9e7959c53a0d068497ce645df387dab93b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 22 Aug 2023 18:13:19 +0200 Subject: [PATCH 112/130] QmlDesigner: Add mechanism to refresh meta infos Change-Id: I894d26387afb32ccf1b8623976f7e6d9b19c0d80 Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- .../designercore/include/abstractview.h | 2 ++ .../designercore/model/abstractview.cpp | 2 ++ .../qmldesigner/designercore/model/model.cpp | 16 +++++++++++- .../qmldesigner/designercore/model/model_p.h | 6 +++++ .../projectstorage/projectstorage.h | 21 +++++++++++++++ .../projectstorage/projectstorageinterface.h | 3 +++ tests/unit/tests/mocks/abstractviewmock.h | 1 + tests/unit/tests/mocks/projectstoragemock.h | 3 +++ .../unit/tests/unittests/model/model-test.cpp | 26 +++++++++++++++++++ .../projectstorage/projectstorage-test.cpp | 12 +++++++++ 10 files changed, 91 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 9035aba0fde..7ca01c687eb 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -148,6 +148,8 @@ public: virtual void modelAttached(Model *model); virtual void modelAboutToBeDetached(Model *model); + virtual void refreshMetaInfos(); + virtual void nodeCreated(const ModelNode &createdNode); virtual void nodeAboutToBeRemoved(const ModelNode &removedNode); virtual void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 2a9e0e12462..2fe422487c9 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -164,6 +164,8 @@ void AbstractView::modelAboutToBeDetached(Model *) removeModel(); } +void AbstractView::refreshMetaInfos() {} + /*! \enum QmlDesigner::AbstractView::PropertyChangeFlag diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index d511ae5d64d..74afaaad86d 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -82,6 +82,8 @@ ModelPrivate::ModelPrivate(Model *model, m_currentStateNode = m_rootInternalNode; m_currentTimelineNode = m_rootInternalNode; + + projectStorage->addRefreshCallback(&m_metaInfoRefreshCallback); } ModelPrivate::ModelPrivate(Model *model, @@ -103,6 +105,8 @@ ModelPrivate::ModelPrivate(Model *model, m_currentStateNode = m_rootInternalNode; m_currentTimelineNode = m_rootInternalNode; + + projectStorage->addRefreshCallback(&m_metaInfoRefreshCallback); } ModelPrivate::ModelPrivate(Model *model, @@ -123,10 +127,15 @@ ModelPrivate::ModelPrivate(Model *model, m_currentTimelineNode = m_rootInternalNode; } -ModelPrivate::~ModelPrivate() = default; +ModelPrivate::~ModelPrivate() +{ + projectStorage->removeRefreshCallback(&m_metaInfoRefreshCallback); +}; void ModelPrivate::detachAllViews() { + projectStorage->removeRefreshCallback(&m_metaInfoRefreshCallback); + for (const QPointer &view : std::as_const(m_viewList)) detachView(view.data(), true); @@ -381,6 +390,11 @@ void ModelPrivate::setTypeId(InternalNode *node, Utils::SmallStringView typeName } } +void ModelPrivate::emitRefreshMetaInfos() +{ + notifyNodeInstanceViewLast([&](AbstractView *view) { view->refreshMetaInfos(); }); +} + void ModelPrivate::handleResourceSet(const ModelResourceSet &resourceSet) { for (const ModelNode &node : resourceSet.removeModelNodes) { diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 8ba7c69b595..1df16d24bb5 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -19,6 +19,7 @@ #include #include +#include QT_BEGIN_NAMESPACE class QPlainTextEdit; @@ -111,6 +112,9 @@ public: ~ModelPrivate() override; + ModelPrivate(const ModelPrivate &) = delete; + ModelPrivate &operator=(const ModelPrivate &) = delete; + QUrl fileUrl() const; void setFileUrl(const QUrl &url); @@ -313,6 +317,7 @@ private: EnabledViewRange enabledViews() const; ImportedTypeNameId importedTypeNameId(Utils::SmallStringView typeName); void setTypeId(InternalNode *node, Utils::SmallStringView typeName); + void emitRefreshMetaInfos(); public: NotNullPointer projectStorage = nullptr; @@ -321,6 +326,7 @@ public: private: Model *m_model = nullptr; MetaInfo m_metaInfo; + std::function m_metaInfoRefreshCallback{[&] { emitRefreshMetaInfos(); }}; Imports m_imports; Imports m_possibleImportList; Imports m_usedImportList; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 1158572e5e0..464d0f3b443 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -109,6 +110,8 @@ public: commonTypeCache_.resetTypeIds(); }); + + callRefreshMetaInfoCallback(); } void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override @@ -120,6 +123,17 @@ public: }); } + void addRefreshCallback(std::function *callback) override + { + m_refreshCallbacks.push_back(callback); + } + + void removeRefreshCallback(std::function *callback) override + { + m_refreshCallbacks.erase( + std::find(m_refreshCallbacks.begin(), m_refreshCallbacks.end(), callback)); + } + ModuleId moduleId(Utils::SmallStringView moduleName) const override { return moduleCache.id(moduleName); @@ -602,6 +616,12 @@ private: return selectAllModulesStatement.template valuesWithTransaction(); } + void callRefreshMetaInfoCallback() + { + for (auto *callback : m_refreshCallbacks) + (*callback)(); + } + class AliasPropertyDeclaration { public: @@ -2767,6 +2787,7 @@ public: Initializer initializer; mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}}; Storage::Info::CommonTypeCache commonTypeCache_{*this}; + std::vector *> m_refreshCallbacks; ReadWriteStatement<1, 3> upsertTypeStatement{ "INSERT INTO types(sourceId, name, traits) VALUES(?1, ?2, ?3) ON CONFLICT DO " "UPDATE SET traits=excluded.traits WHERE traits IS NOT " diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index 49ae0637936..daf4225ffab 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -27,6 +27,9 @@ public: virtual void synchronize(Storage::Synchronization::SynchronizationPackage package) = 0; virtual void synchronizeDocumentImports(const Storage::Imports imports, SourceId sourceId) = 0; + virtual void addRefreshCallback(std::function *callback) = 0; + virtual void removeRefreshCallback(std::function *callback) = 0; + virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0; virtual std::optional propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0; diff --git a/tests/unit/tests/mocks/abstractviewmock.h b/tests/unit/tests/mocks/abstractviewmock.h index ed97e7e443c..f01c9f5f64b 100644 --- a/tests/unit/tests/mocks/abstractviewmock.h +++ b/tests/unit/tests/mocks/abstractviewmock.h @@ -56,4 +56,5 @@ public: AbstractView::PropertyChangeFlags propertyChange), (override)); MOCK_METHOD(void, nodeAboutToBeRemoved, (const QmlDesigner::ModelNode &removedNode), (override)); + MOCK_METHOD(void, refreshMetaInfos, (), (override)); }; diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index 61788b46db8..22e316ede3b 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -95,6 +95,9 @@ public: (const QmlDesigner::Storage::Imports imports, QmlDesigner::SourceId sourceId), (override)); + MOCK_METHOD(void, addRefreshCallback, (std::function * callback), (override)); + MOCK_METHOD(void, removeRefreshCallback, (std::function * callback), (override)); + MOCK_METHOD(QmlDesigner::ModuleId, moduleId, (::Utils::SmallStringView), (const, override)); MOCK_METHOD(std::optional, diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index 52cc04564a8..17374d4314c 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -869,4 +869,30 @@ TEST_F(Model, meta_info_of_not_existing_type_is_invalid) ASSERT_THAT(meta_info, IsFalse()); } +TEST_F(Model, add_refresh_callback_to_project_storage) +{ + EXPECT_CALL(projectStorageMock, addRefreshCallback(_)); + + QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", -1, -1, nullptr, {}}; +} + +TEST_F(Model, remove_refresh_callback_from_project_storage) +{ + EXPECT_CALL(projectStorageMock, removeRefreshCallback(_)).Times(2); // there is a model in the fixture + + QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", -1, -1, nullptr, {}}; +} + +TEST_F(Model, refresh_callback_is_calling_abstract_view) +{ + std::function *callback = nullptr; + ON_CALL(projectStorageMock, addRefreshCallback(_)).WillByDefault([&](auto *c) { callback = c; }); + QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", -1, -1, nullptr, {}}; + model.attachView(&viewMock); + + EXPECT_CALL(viewMock, refreshMetaInfos()); + + (*callback)(); +} + } // namespace diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index e666493e60c..059c49e1397 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -7192,4 +7192,16 @@ TEST_F(ProjectStorage, synchronize_property_editor_with_non_existing_type_name) ASSERT_THAT(storage.propertyEditorPathId(fetchTypeId(sourceId4, "Item4D")), IsFalse()); } +TEST_F(ProjectStorage, call_refresh_callback_after_synchronization) +{ + auto package{createSimpleSynchronizationPackage()}; + MockFunction callbackMock; + auto callback = callbackMock.AsStdFunction(); + storage.addRefreshCallback(&callback); + + EXPECT_CALL(callbackMock, Call()); + + storage.synchronize(package); +} + } // namespace From 1177bb4b1f3f913006d5ff0f2f05dfef2dc3a1de Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 23 Aug 2023 23:45:52 +0200 Subject: [PATCH 113/130] QmlDesigner: Add PropertyComponentGenerator Task-number: QDS-10578 Change-Id: I2c20f5aaa6ebfe7736012a02d4c4596589007866 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- .../PropertyTemplates/TemplateTypes.qml | 25 +- src/libs/qmljs/qmljssimplereader.cpp | 5 +- src/libs/qmljs/qmljssimplereader.h | 3 +- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../propertycomponentgenerator.cpp | 272 ++++++++++ .../propertycomponentgenerator.h | 53 ++ .../propertycomponentgeneratorinterface.h | 46 ++ .../propertyeditorcomponentgenerator.cpp | 177 +++++++ .../propertyeditorcomponentgenerator.h | 29 ++ .../qmldesigner/designercore/include/model.h | 6 + .../qmldesigner/designercore/include/module.h | 35 ++ .../qmldesigner/designercore/model/model.cpp | 19 + tests/unit/tests/matchers/CMakeLists.txt | 1 + .../tests/matchers/strippedstring-matcher.h | 81 +++ tests/unit/tests/mocks/CMakeLists.txt | 2 + tests/unit/tests/mocks/projectstoragemock.cpp | 25 +- tests/unit/tests/mocks/projectstoragemock.h | 24 +- .../mocks/propertycomponentgeneratormock.h | 22 + .../unit/tests/printers/gtest-qt-printing.cpp | 5 + tests/unit/tests/printers/gtest-qt-printing.h | 2 + .../tests/testdesignercore/CMakeLists.txt | 1 + tests/unit/tests/unittests/CMakeLists.txt | 1 + .../unittests/componentcore/CMakeLists.txt | 15 + .../propertycomponentgenerator-test.cpp | 322 ++++++++++++ .../propertyeditorcomponentgenerator-test.cpp | 487 ++++++++++++++++++ .../unittests/metainfo/nodemetainfo-test.cpp | 2 +- .../unit/tests/unittests/model/model-test.cpp | 43 ++ 27 files changed, 1689 insertions(+), 15 deletions(-) create mode 100644 src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp create mode 100644 src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.h create mode 100644 src/plugins/qmldesigner/components/componentcore/propertycomponentgeneratorinterface.h create mode 100644 src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.cpp create mode 100644 src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.h create mode 100644 src/plugins/qmldesigner/designercore/include/module.h create mode 100644 tests/unit/tests/matchers/strippedstring-matcher.h create mode 100644 tests/unit/tests/mocks/propertycomponentgeneratormock.h create mode 100644 tests/unit/tests/unittests/componentcore/CMakeLists.txt create mode 100644 tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp create mode 100644 tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TemplateTypes.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TemplateTypes.qml index a6ee7eb476a..03e42a974fe 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TemplateTypes.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TemplateTypes.qml @@ -9,72 +9,93 @@ AutoTypes { Type { typeNames: ["int"] + module: "QML" sourceFile: "IntEditorTemplate.template" } Type { typeNames: ["real", "double", "qreal"] + module: "QML" sourceFile: "RealEditorTemplate.template" } Type { typeNames: ["string", "QString"] + module: "QML" sourceFile: "StringEditorTemplate.template" } Type { - typeNames: ["QUrl", "url"] + typeNames: ["url", "QUrl"] + module: "QML" sourceFile: "UrlEditorTemplate.template" } Type { typeNames: ["bool", "boolean"] + module: "QML" sourceFile: "BooleanEditorTemplate.template" } Type { typeNames: ["color", "QColor"] + module: "QtQuick" sourceFile: "ColorEditorTemplate.template" } Type { typeNames: ["Text"] + module: "QtQuick" sourceFile: "TextEditorTemplate.template" separateSection: true } Type { typeNames: ["font", "QFont"] + module: "QtQuick" sourceFile: "FontEditorTemplate.template" separateSection: true } Type { typeNames: ["Rectangle"] + module: "QtQuick" sourceFile: "RectangleEditorTemplate.template" separateSection: true } Type { typeNames: ["Image"] + module: "QtQuick" sourceFile: "ImageEditorTemplate.template" separateSection: true } Type { - typeNames: ["TextureInput", "Texture"] + typeNames: ["TextureInput"] + module: "QtQuick3D" + sourceFile: "3DItemFilterComboBoxEditorTemplate.template" + needsTypeArg: true + } + + Type { + typeNames: ["Texture"] + module: "QtQuick3D" sourceFile: "3DItemFilterComboBoxEditorTemplate.template" needsTypeArg: true } Type { typeNames: ["vector2d"] + module: "QtQuick3D" sourceFile: "Vector2dEditorTemplate.template" } Type { typeNames: ["vector3d"] + module: "QtQuick3D" sourceFile: "Vector3dEditorTemplate.template" } Type { typeNames: ["vector4d"] + module: "QtQuick3D" sourceFile: "Vector4dEditorTemplate.template" } } diff --git a/src/libs/qmljs/qmljssimplereader.cpp b/src/libs/qmljs/qmljssimplereader.cpp index 761131b3a28..d4b8a21bf7c 100644 --- a/src/libs/qmljs/qmljssimplereader.cpp +++ b/src/libs/qmljs/qmljssimplereader.cpp @@ -89,7 +89,10 @@ static Q_LOGGING_CATEGORY(simpleReaderLog, "qtc.qmljs.simpleReader", QtWarningMs return newNode; } - const SimpleReaderNode::List SimpleReaderNode::children() const { return m_children; } + const SimpleReaderNode::List &SimpleReaderNode::children() const + { + return m_children; + } void SimpleReaderNode::setProperty(const QString &name, const SourceLocation &nameLocation, diff --git a/src/libs/qmljs/qmljssimplereader.h b/src/libs/qmljs/qmljssimplereader.h index de57bb62dad..e14f49d65dc 100644 --- a/src/libs/qmljs/qmljssimplereader.h +++ b/src/libs/qmljs/qmljssimplereader.h @@ -34,6 +34,7 @@ public: SourceLocation valueLocation; bool isValid() const { return !value.isNull() && value.isValid(); } + explicit operator bool() const { return isValid(); } bool isDefaultValue() const { return !value.isNull() && !nameLocation.isValid() && !valueLocation.isValid(); @@ -53,7 +54,7 @@ public: WeakPtr parent() const; QString name() const; SourceLocation nameLocation() const; - const List children() const; + const List &children() const; protected: SimpleReaderNode(); diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 8ea79bcdcf8..6df5a632472 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -238,6 +238,7 @@ extend_qtc_library(QmlDesignerCore modelmerger.h modelnode.h modelnodepositionstorage.h + module.h nodeabstractproperty.h nodeinstance.h nodelistproperty.h diff --git a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp new file mode 100644 index 00000000000..7e5487909a8 --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp @@ -0,0 +1,272 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "propertycomponentgenerator.h" + +#include +#include + +#include + +#include +#include + +#include + +using namespace Qt::StringLiterals; + +namespace QmlDesigner { + +namespace { + +QmlJS::SimpleReaderNode::Ptr createTemplateConfiguration(const QString &propertyEditorResourcesPath) +{ + QmlJS::SimpleReader reader; + const QString fileName = propertyEditorResourcesPath + u"/PropertyTemplates/TemplateTypes.qml"; + auto templateConfiguration = reader.readFile(fileName); + + if (!templateConfiguration) + qWarning().nospace() << "template definitions:" << reader.errors(); + + return templateConfiguration; +} + +template +Type getProperty(const QmlJS::SimpleReaderNode *node, const QString &name) +{ + if (auto property = node->property(name)) { + const auto &value = property.value; + if (value.type() == QVariant::List) { + auto list = value.toList(); + if (list.size()) + return list.front().value(); + } else { + return property.value.value(); + } + } + + return {}; +} + +QString getContent(const QString &path) +{ + QFile file{path}; + + if (file.open(QIODevice::ReadOnly)) + return QString::fromUtf8(file.readAll()); + + return {}; +} + +QString generateComponentText(Utils::SmallStringView propertyName, + QStringView source, + Utils::SmallStringView typeName, + bool needsTypeArg) +{ + QString underscoreName{propertyName}; + underscoreName.replace('.', '_'); + if (needsTypeArg) + return source.arg(QString{propertyName}, underscoreName, QString{typeName}); + + return source.arg(QString{propertyName}, underscoreName); +} + +PropertyComponentGenerator::Property generate(const PropertyMetaInfo &property, + const PropertyComponentGenerator::Entry &entry) +{ + auto propertyName = property.name(); + auto component = generateComponentText(propertyName, + entry.propertyTemplate, + entry.typeName, + entry.needsTypeArg); + + if (entry.separateSection) + return PropertyComponentGenerator::ComplexProperty{propertyName, component}; + + return PropertyComponentGenerator::BasicProperty{propertyName, component}; +} + +auto getRandomExportedName(const NodeMetaInfo &metaInfo) +{ + const auto &names = metaInfo.allExportedTypeNames(); + + using Result = decltype(names.front().name); + + if (!names.empty()) + return names.front().name; + + return Result{}; +} + +} // namespace + +QString PropertyComponentGenerator::generateSubComponentText(Utils::SmallStringView propertyBaseName, + const PropertyMetaInfo &subProperty) const +{ + auto propertyType = subProperty.propertyType(); + if (auto entry = findEntry(propertyType)) { + auto propertyName = Utils::SmallString::join({propertyBaseName, subProperty.name()}); + return generateComponentText(propertyName, + entry->propertyTemplate, + entry->typeName, + entry->needsTypeArg); + } + + return {}; +} + +QString PropertyComponentGenerator::generateComplexComponentText(Utils::SmallStringView propertyName, + const NodeMetaInfo &propertyType) const +{ + auto subProperties = propertyType.properties(); + + if (std::empty(subProperties)) + return {}; + + auto propertyTypeName = getRandomExportedName(propertyType); + static QString templateText = QStringLiteral( + R"xy( + Section { + caption: %1 - %2 + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 8 + rightPadding: 0 + expanded: false + level: 1 + SectionLayout { + )xy"); + + auto component = templateText.arg(QString{propertyName}, QString{propertyTypeName}); + + auto propertyBaseName = Utils::SmallString::join({propertyName, "."}); + + bool subPropertiesHaveContent = false; + for (const auto &subProperty : propertyType.properties()) { + auto subPropertyContent = generateSubComponentText(propertyBaseName, subProperty); + subPropertiesHaveContent = subPropertiesHaveContent || subPropertyContent.size(); + component += subPropertyContent; + } + + component += "}\n}\n"_L1; + + if (subPropertiesHaveContent) + return component; + + return {}; +} + +PropertyComponentGenerator::Property PropertyComponentGenerator::generateComplexComponent( + const PropertyMetaInfo &property, const NodeMetaInfo &propertyType) const +{ + auto propertyName = property.name(); + auto component = generateComplexComponentText(propertyName, propertyType); + + if (component.isEmpty()) + return {}; + + return ComplexProperty{propertyName, component}; +} + +namespace { + +std::optional createEntry(QmlJS::SimpleReaderNode *node, + Model *model, + const QString &templatesPath) +{ + auto moduleName = getProperty(node, "module"); + if (moduleName.isEmpty()) + return {}; + + auto module = model->module(moduleName); + + auto typeName = getProperty(node, "typeNames"); + + auto type = model->metaInfo(module, typeName); + if (!type) + return {}; + + auto path = getProperty(node, "sourceFile"); + if (path.isEmpty()) + return {}; + + auto content = getContent(templatesPath + path); + if (content.isEmpty()) + return {}; + + bool needsTypeArg = getProperty(node, "needsTypeArg"); + + bool separateSection = getProperty(node, "separateSection"); + + return PropertyComponentGenerator::Entry{std::move(type), + std::move(typeName), + std::move(content), + separateSection, + needsTypeArg}; +} + +PropertyComponentGenerator::Entries createEntries(QmlJS::SimpleReaderNode::Ptr templateConfiguration, + Model *model, + const QString &templatesPath) +{ + PropertyComponentGenerator::Entries entries; + entries.reserve(32); + + const auto &nodes = templateConfiguration->children(); + for (const QmlJS::SimpleReaderNode::Ptr &node : nodes) { + if (auto entry = createEntry(node.get(), model, templatesPath)) + entries.push_back(*entry); + } + + return entries; +} + +QStringList createImports(QmlJS::SimpleReaderNode::Ptr templateConfiguration) +{ + auto property = templateConfiguration->property("imports"); + return Utils::transform(property.value.toList(), + [](const auto &entry) { return entry.toString(); }); +} + +} // namespace + +PropertyComponentGenerator::PropertyComponentGenerator(const QString &propertyEditorResourcesPath, + Model *model) + : m_entries(createEntries(createTemplateConfiguration(propertyEditorResourcesPath), + model, + propertyEditorResourcesPath + "/PropertyTemplates/")) +{ + auto templateConfiguration = createTemplateConfiguration(propertyEditorResourcesPath); + + m_entries = createEntries(templateConfiguration, + model, + propertyEditorResourcesPath + "/PropertyTemplates/"); + + m_imports = createImports(templateConfiguration); +} + +PropertyComponentGenerator::Property PropertyComponentGenerator::create(const PropertyMetaInfo &property) const +{ + auto propertyType = property.propertyType(); + if (auto entry = findEntry(propertyType)) + return generate(property, *entry); + + if (property.isWritable() && property.isPointer()) + return {}; + + return generateComplexComponent(property, propertyType); +} + +const PropertyComponentGenerator::Entry *PropertyComponentGenerator::findEntry(const NodeMetaInfo &type) const +{ + auto found = std::find_if(m_entries.begin(), m_entries.end(), [&](const auto &entry) { + return entry.type == type; + }); + + if (found != m_entries.end()) + return std::addressof(*found); + + return nullptr; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.h b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.h new file mode 100644 index 00000000000..67239c16a70 --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.h @@ -0,0 +1,53 @@ +// 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 "propertycomponentgeneratorinterface.h" + +#include +#include + +#include + +#include +#include +#include + +namespace QmlDesigner { + +class PropertyComponentGenerator final : public PropertyComponentGeneratorInterface +{ +public: + PropertyComponentGenerator(const QString &propertyEditorResourcesPath, Model *model); + + Property create(const PropertyMetaInfo &property) const override; + + struct Entry + { + NodeMetaInfo type; + Utils::SmallString typeName; + QString propertyTemplate; + bool separateSection = false; + bool needsTypeArg = false; + }; + + using Entries = std::vector; + + QStringList imports() const override { return m_imports; } + +private: + const Entry *findEntry(const NodeMetaInfo &type) const; + QString generateSubComponentText(Utils::SmallStringView propertyBaseName, + const PropertyMetaInfo &subProperty) const; + QString generateComplexComponentText(Utils::SmallStringView propertyName, + const NodeMetaInfo &propertyType) const; + Property generateComplexComponent(const PropertyMetaInfo &property, + const NodeMetaInfo &propertyType) const; + +private: + Entries m_entries; + QStringList m_imports; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/propertycomponentgeneratorinterface.h b/src/plugins/qmldesigner/components/componentcore/propertycomponentgeneratorinterface.h new file mode 100644 index 00000000000..ab4047c957b --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/propertycomponentgeneratorinterface.h @@ -0,0 +1,46 @@ +// 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 +#include + +#include + +#include +#include +#include + +namespace QmlDesigner { + +class PropertyComponentGeneratorInterface +{ +public: + PropertyComponentGeneratorInterface() = default; + PropertyComponentGeneratorInterface(const PropertyComponentGeneratorInterface &) = delete; + PropertyComponentGeneratorInterface &operator=(const PropertyComponentGeneratorInterface &) = delete; + + struct BasicProperty + { + Utils::SmallString propertyName; + QString component; + }; + + struct ComplexProperty + { + Utils::SmallString propertyName; + QString component; + }; + + using Property = std::variant; + + virtual Property create(const PropertyMetaInfo &property) const = 0; + + virtual QStringList imports() const = 0; + +protected: + ~PropertyComponentGeneratorInterface() = default; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.cpp b/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.cpp new file mode 100644 index 00000000000..272130b8f33 --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.cpp @@ -0,0 +1,177 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "propertyeditorcomponentgenerator.h" + +#include +#include + +#include + +namespace QmlDesigner { + +namespace { + +using GeneratorProperty = PropertyComponentGeneratorInterface::Property; +using BasicProperty = PropertyComponentGeneratorInterface::BasicProperty; +using ComplexProperty = PropertyComponentGeneratorInterface::ComplexProperty; +using GeneratorProperties = std::vector; + +QString createImports(const QStringList &imports) +{ + QString importsContent; + importsContent.reserve(512); + + for (const QString &import : imports) { + importsContent += import; + importsContent += '\n'; + } + + return importsContent; +} + +QString componentButton(bool isComponent) +{ + if (isComponent) + return "ComponentButton {}"; + + return {}; +} + +void createBasicPropertySections(QString &components, + Utils::span generatorProperties) +{ + components += R"xy( + Column { + width: parent.width + leftPadding: 8 + bottomPadding: 10 + SectionLayout {)xy"; + + for (const auto &generatorProperty : generatorProperties) + components += std::get_if(&generatorProperty)->component; + + components += R"xy( + } + })xy"; +} + +void createComplexPropertySections(QString &components, + Utils::span generatorProperties) +{ + for (const auto &generatorProperty : generatorProperties) + components += std::get_if(&generatorProperty)->component; +} + +Utils::SmallStringView propertyName(const GeneratorProperty &property) +{ + return std::visit( + [](const auto &p) -> Utils::SmallStringView { + if constexpr (!std::is_same_v, std::monostate>) + return p.propertyName; + else + return {}; + }, + property); +} + +PropertyMetaInfos getUnmangedProperties(const NodeMetaInfo &nodeInfo) +{ + PropertyMetaInfos properties; + properties.reserve(128); + + auto prototypes = nodeInfo.selfAndPrototypes(); + + for (const auto &prototype : prototypes) { + if (prototype.propertyEditorPathId()) + break; + + auto localProperties = prototype.localProperties(); + + properties.insert(properties.end(), localProperties.begin(), localProperties.end()); + } + + return properties; +} + +GeneratorProperties createSortedGeneratorProperties( + const PropertyMetaInfos &properties, const PropertyComponentGeneratorType &propertyGenerator) +{ + GeneratorProperties generatorProperties; + generatorProperties.reserve(properties.size()); + + for (const auto &property : properties) { + auto generatedProperty = propertyGenerator.create(property); + if (!std::holds_alternative(generatedProperty)) + generatorProperties.push_back(std::move(generatedProperty)); + } + std::sort(generatorProperties.begin(), + generatorProperties.end(), + [](const auto &first, const auto &second) { + // this is sensitive to the order of the variant types but the test should catch any error + return std::forward_as_tuple(first.index(), propertyName(first)) + < std::forward_as_tuple(second.index(), propertyName(second)); + }); + + return generatorProperties; +} + +QString createPropertySections(const PropertyComponentGeneratorType &propertyGenerator, + const NodeMetaInfo &nodeInfo) +{ + QString propertyComponents; + propertyComponents.reserve(100000); + + auto generatorProperties = createSortedGeneratorProperties(getUnmangedProperties(nodeInfo), + propertyGenerator); + + const auto begin = generatorProperties.begin(); + const auto end = generatorProperties.end(); + + auto point = std::partition_point(begin, end, [](const auto &p) { + return !std::holds_alternative(p); + }); + + if (begin != point) + createBasicPropertySections(propertyComponents, Utils::span{begin, point}); + + if (point != end) + createComplexPropertySections(propertyComponents, Utils::span{point, end}); + + return propertyComponents; +} + +} // namespace + +PropertyEditorTemplateGenerator::PropertyEditorTemplateGenerator( + const PropertyComponentGeneratorType &propertyGenerator) + : m_propertyGenerator{propertyGenerator} +{} + +QString PropertyEditorTemplateGenerator::create(const NodeMetaInfo &nodeInfo, bool isComponent) +{ + return QString{R"xy( + %1 + Column { + width: parent.width + %2 + Section { + caption: "%3" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + %4 + } + } + })xy"} + .arg(createImports(m_propertyGenerator.imports()), + componentButton(isComponent), + QObject::tr("Exposed Custom Properties"), + createPropertySections(m_propertyGenerator, nodeInfo)); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.h b/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.h new file mode 100644 index 00000000000..37dd824d43f --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.h @@ -0,0 +1,29 @@ +// 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 "propertycomponentgenerator.h" + +#include + +namespace QmlDesigner { + +#ifdef UNIT_TESTS +using PropertyComponentGeneratorType = PropertyComponentGeneratorInterface; +#else +using PropertyComponentGeneratorType = PropertyComponentGenerator; +#endif +class PropertyEditorTemplateGenerator +{ + +public: + PropertyEditorTemplateGenerator(const PropertyComponentGeneratorType &propertyGenerator); + + QString create(const NodeMetaInfo &nodeInfo, bool isComponent); + +private: + const PropertyComponentGeneratorType &m_propertyGenerator; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index c404525a9c0..ac8182c0760 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -7,7 +7,9 @@ #include #include +#include #include +#include #include #include @@ -125,7 +127,11 @@ public: const MetaInfo metaInfo() const; MetaInfo metaInfo(); + Module module(Utils::SmallStringView moduleName); NodeMetaInfo metaInfo(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const; + NodeMetaInfo metaInfo(Module module, + Utils::SmallStringView typeName, + Storage::Version version = Storage::Version{}) const; bool hasNodeMetaInfo(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const; void setMetaInfo(const MetaInfo &metaInfo); diff --git a/src/plugins/qmldesigner/designercore/include/module.h b/src/plugins/qmldesigner/designercore/include/module.h new file mode 100644 index 00000000000..5078f3ff8f5 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/include/module.h @@ -0,0 +1,35 @@ +// 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 +#include + +namespace QmlDesigner { + +class Module +{ +public: + Module() = default; + + Module(ModuleId moduleId, NotNullPointer projectStorage) + : m_projectStorage{projectStorage} + , m_id{moduleId} + {} + + ModuleId id() const { return m_id; } + + bool isValid() const { return bool(m_id); } + + explicit operator bool() const { return isValid(); } + + const ProjectStorageType &projectStorage() const { return *m_projectStorage; } + + friend bool operator==(Module first, Module second) { return first.m_id == second.m_id; } + +private: + NotNullPointer m_projectStorage = {}; + ModuleId m_id; +}; +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 74afaaad86d..c0393d524c1 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -2436,6 +2436,16 @@ NodeMetaInfo Model::metaInfo(const TypeName &typeName, int majorVersion, int min } } +NodeMetaInfo Model::metaInfo(Module module, Utils::SmallStringView typeName, Storage::Version version) const +{ + if constexpr (useProjectStorage()) { + return NodeMetaInfo(d->projectStorage->typeId(module.id(), typeName, version), + d->projectStorage); + } else { + return {}; + } +} + /*! \brief Returns list of QML types available within the model. */ @@ -2444,6 +2454,15 @@ MetaInfo Model::metaInfo() return d->metaInfo(); } +Module Model::module(Utils::SmallStringView moduleName) +{ + if constexpr (useProjectStorage()) { + return Module(d->projectStorage->moduleId(moduleName), d->projectStorage); + } + + return {}; +} + /*! \name View related functions */ //\{ diff --git a/tests/unit/tests/matchers/CMakeLists.txt b/tests/unit/tests/matchers/CMakeLists.txt index f050cb83c85..32a7dad99c3 100644 --- a/tests/unit/tests/matchers/CMakeLists.txt +++ b/tests/unit/tests/matchers/CMakeLists.txt @@ -7,6 +7,7 @@ add_qtc_library(TestMatchers OBJECT SOURCES info_exportedtypenames-matcher.h import-matcher.h + strippedstring-matcher.h unittest-matchers.h version-matcher.h qvariant-matcher.h diff --git a/tests/unit/tests/matchers/strippedstring-matcher.h b/tests/unit/tests/matchers/strippedstring-matcher.h new file mode 100644 index 00000000000..3c491cba8f8 --- /dev/null +++ b/tests/unit/tests/matchers/strippedstring-matcher.h @@ -0,0 +1,81 @@ +// 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 "version-matcher.h" + +#include + +#include + +#include + +namespace Internal { +class StippedStringEqMatcher +{ +public: + explicit StippedStringEqMatcher(QString content) + : m_content(strip(std::move(content))) + {} + + bool MatchAndExplain(const QString &s, testing::MatchResultListener *listener) const + { + auto strippedContent = strip(s); + bool isEqual = m_content == strippedContent; + + if (!isEqual) { + auto [found1, found2] = std::mismatch(strippedContent.begin(), + strippedContent.end(), + m_content.begin(), + m_content.end()); + if (found1 != strippedContent.end() && found2 != m_content.end()) { + *listener << "\ncurrent value mismatch start: \n" + << QStringView{found1, strippedContent.end()}; + *listener << "\nexpected value mismatch start: \n" + << QStringView{found2, m_content.end()}; + } + } + + return isEqual; + } + + static QString strip(QString s) + { + s.replace('\n', ' '); + + auto newEnd = std::unique(s.begin(), s.end(), [](QChar first, QChar second) { + return first.isSpace() && second.isSpace(); + }); + + s.erase(newEnd, s.end()); + + static QRegularExpression regex1{R"xx((\W)\s)xx"}; + s.replace(regex1, R"xx(\1)xx"); + + static QRegularExpression regex2{R"xx(\s(\W))xx"}; + s.replace(regex2, R"xx(\1)xx"); + + return s.trimmed(); + } + + void DescribeTo(::std::ostream *os) const + { + *os << "has stripped content " << testing::PrintToString(m_content); + } + + void DescribeNegationTo(::std::ostream *os) const + { + *os << "doesn't have stripped content " << testing::PrintToString(m_content); + } + +private: + const QString m_content; +}; + +} // namespace Internal + +inline auto StrippedStringEq(const QStringView &content) +{ + return ::testing::PolymorphicMatcher(Internal::StippedStringEqMatcher(content.toString())); +} diff --git a/tests/unit/tests/mocks/CMakeLists.txt b/tests/unit/tests/mocks/CMakeLists.txt index 5323f09e407..bd34e15c68c 100644 --- a/tests/unit/tests/mocks/CMakeLists.txt +++ b/tests/unit/tests/mocks/CMakeLists.txt @@ -2,6 +2,7 @@ add_qtc_library(TestMocks OBJECT EXCLUDE_FROM_INSTALL PROPERTIES SKIP_AUTOGEN ON PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR} + INCLUDES ../../../../src/plugins/qmldesigner/components/componentcore DEPENDS Qt::Core Qt::Widgets Googletest Sqlite TestDesignerCore SOURCES @@ -20,6 +21,7 @@ add_qtc_library(TestMocks OBJECT mocktimer.h mocktimestampprovider.h modelresourcemanagementmock.h + propertycomponentgeneratormock.h projectstoragemock.cpp projectstoragemock.h projectstoragepathwatchermock.h diff --git a/tests/unit/tests/mocks/projectstoragemock.cpp b/tests/unit/tests/mocks/projectstoragemock.cpp index 105b8c73469..b2445cd82ac 100644 --- a/tests/unit/tests/mocks/projectstoragemock.cpp +++ b/tests/unit/tests/mocks/projectstoragemock.cpp @@ -112,6 +112,16 @@ QmlDesigner::ImportId ProjectStorageMock::createImportId(QmlDesigner::ModuleId m return importId; } +void ProjectStorageMock::addExportedTypeName(QmlDesigner::TypeId typeId, + QmlDesigner::ModuleId moduleId, + Utils::SmallStringView typeName) +{ + ON_CALL(*this, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId)); + ON_CALL(*this, fetchTypeIdByModuleIdAndExportedName(Eq(moduleId), Eq(typeName))) + .WillByDefault(Return(typeId)); + exportedTypeName[typeId].emplace_back(moduleId, typeName); +} + PropertyDeclarationId ProjectStorageMock::createProperty(TypeId typeId, Utils::SmallString name, PropertyDeclarationTraits traits, @@ -159,6 +169,12 @@ void ProjectStorageMock::createFunction(QmlDesigner::TypeId typeId, Utils::Small ON_CALL(*this, functionDeclarationNames(Eq(typeId))).WillByDefault(Return(functionNames)); } +void ProjectStorageMock::setPropertyEditorPathId(QmlDesigner::TypeId typeId, + QmlDesigner::SourceId sourceId) +{ + ON_CALL(*this, propertyEditorPathId(Eq(typeId))).WillByDefault(Return(sourceId)); +} + namespace { void addBaseProperties(TypeId typeId, TypeIds baseTypeIds, ProjectStorageMock &storage) { @@ -192,6 +208,10 @@ TypeId ProjectStorageMock::createType(ModuleId moduleId, ON_CALL(*this, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId)); ON_CALL(*this, fetchTypeIdByModuleIdAndExportedName(Eq(moduleId), Eq(typeName))) .WillByDefault(Return(typeId)); + addExportedTypeName(typeId, moduleId, typeName); + ON_CALL(*this, exportedTypeNames(Eq(typeId))).WillByDefault([&](TypeId id) { + return exportedTypeName[id]; + }); PropertyDeclarationId defaultPropertyDeclarationId; if (defaultPropertyName.size()) { if (!defaultPropertyTypeId) { @@ -280,6 +300,7 @@ void ProjectStorageMock::setupQtQuick() auto intId = createValue(qmlModuleId, "int"); createValue(qmlNativeModuleId, "uint"); auto doubleId = createValue(qmlModuleId, "double"); + addExportedTypeName(doubleId, qmlModuleId, "real"); createValue(qmlNativeModuleId, "float"); createValue(qmlModuleId, "date"); auto stringId = createValue(qmlModuleId, "string"); @@ -307,7 +328,7 @@ void ProjectStorageMock::setupQtQuick() createProperty(fontValueTypeId, "overline", boolId); createProperty(fontValueTypeId, "pointSize", doubleId); createProperty(fontValueTypeId, "pixelSize", intId); - createValue(qtQuickModuleId, "font", {fontValueTypeId}); + auto fontId = createValue(qtQuickModuleId, "font", {fontValueTypeId}); auto itemId = createObject(qtQuickModuleId, "Item", @@ -315,6 +336,8 @@ void ProjectStorageMock::setupQtQuick() PropertyDeclarationTraits::IsList, qtObjectId, {qtObjectId}); + createProperty(itemId, "x", doubleId); + createProperty(itemId, "font", fontId); auto inputDeviceId = createObject(qtQuickModuleId, "InputDevice", {qtObjectId}); createProperty(inputDeviceId, "seatName", stringId); diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index 22e316ede3b..5c189af519f 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -40,15 +41,18 @@ public: QmlDesigner::SourceId sourceId, QmlDesigner::Storage::Version version = QmlDesigner::Storage::Version{}); - QmlDesigner::TypeId createType( - QmlDesigner::ModuleId moduleId, - Utils::SmallStringView typeName, - Utils::SmallStringView defaultPropertyName, - QmlDesigner::Storage::PropertyDeclarationTraits defaultPropertyTraits, - QmlDesigner::TypeId defaultPropertyTypeId, - QmlDesigner::Storage::TypeTraits typeTraits, - QmlDesigner::TypeIds baseTypeIds = {}, - QmlDesigner::SourceId sourceId = QmlDesigner::SourceId{}); + void addExportedTypeName(QmlDesigner::TypeId typeId, + QmlDesigner::ModuleId moduleId, + Utils::SmallStringView typeName); + + QmlDesigner::TypeId createType(QmlDesigner::ModuleId moduleId, + Utils::SmallStringView typeName, + Utils::SmallStringView defaultPropertyName, + QmlDesigner::Storage::PropertyDeclarationTraits defaultPropertyTraits, + QmlDesigner::TypeId defaultPropertyTypeId, + QmlDesigner::Storage::TypeTraits typeTraits, + QmlDesigner::TypeIds baseTypeIds = {}, + QmlDesigner::SourceId sourceId = QmlDesigner::SourceId{}); QmlDesigner::TypeId createType(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName, @@ -85,6 +89,7 @@ public: void createSignal(QmlDesigner::TypeId typeId, Utils::SmallString name); void createFunction(QmlDesigner::TypeId typeId, Utils::SmallString name); + void setPropertyEditorPathId(QmlDesigner::TypeId typeId, QmlDesigner::SourceId sourceId); MOCK_METHOD(void, synchronize, @@ -280,6 +285,7 @@ public: (const, override)); QmlDesigner::Storage::Info::CommonTypeCache typeCache{*this}; + std::map exportedTypeName; }; class ProjectStorageMockWithQtQtuick : public ProjectStorageMock diff --git a/tests/unit/tests/mocks/propertycomponentgeneratormock.h b/tests/unit/tests/mocks/propertycomponentgeneratormock.h new file mode 100644 index 00000000000..043b64b464e --- /dev/null +++ b/tests/unit/tests/mocks/propertycomponentgeneratormock.h @@ -0,0 +1,22 @@ +// 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 "../utils/googletest.h" + +#include + +class PropertyComponentGeneratorMock : public QmlDesigner::PropertyComponentGeneratorInterface +{ + using Property = QmlDesigner::PropertyComponentGeneratorInterface::Property; + +public: + virtual ~PropertyComponentGeneratorMock() = default; + MOCK_METHOD(Property, + create, + (const QmlDesigner::PropertyMetaInfo &), + + (const, override)); + MOCK_METHOD(QStringList, imports, (), (const, override)); +}; diff --git a/tests/unit/tests/printers/gtest-qt-printing.cpp b/tests/unit/tests/printers/gtest-qt-printing.cpp index 532aef525f5..f432d87398c 100644 --- a/tests/unit/tests/printers/gtest-qt-printing.cpp +++ b/tests/unit/tests/printers/gtest-qt-printing.cpp @@ -33,6 +33,11 @@ std::ostream &operator<<(std::ostream &out, const QString &text) return out << text.toUtf8(); } +std::ostream &operator<<(std::ostream &out, QStringView text) +{ + return out << text.toString(); +} + std::ostream &operator<<(std::ostream &out, const QVariant &variant) { QString output; diff --git a/tests/unit/tests/printers/gtest-qt-printing.h b/tests/unit/tests/printers/gtest-qt-printing.h index c84697c17b0..ceec63817cc 100644 --- a/tests/unit/tests/printers/gtest-qt-printing.h +++ b/tests/unit/tests/printers/gtest-qt-printing.h @@ -11,12 +11,14 @@ QT_BEGIN_NAMESPACE class QVariant; class QString; +class QStringView; class QTextCharFormat; class QImage; class QIcon; std::ostream &operator<<(std::ostream &out, const QVariant &QVariant); std::ostream &operator<<(std::ostream &out, const QString &text); +std::ostream &operator<<(std::ostream &out, QStringView text); std::ostream &operator<<(std::ostream &out, const QByteArray &byteArray); std::ostream &operator<<(std::ostream &out, const QTextCharFormat &format); std::ostream &operator<<(std::ostream &out, const QImage &image); diff --git a/tests/unit/tests/testdesignercore/CMakeLists.txt b/tests/unit/tests/testdesignercore/CMakeLists.txt index 9a10b6d7449..0847caa3230 100644 --- a/tests/unit/tests/testdesignercore/CMakeLists.txt +++ b/tests/unit/tests/testdesignercore/CMakeLists.txt @@ -65,6 +65,7 @@ add_qtc_library(TestDesignerCore OBJECT include/metainfo.h include/metainforeader.h include/modelnode.h + include/module.h include/nodeabstractproperty.h include/nodelistproperty.h include/nodemetainfo.h diff --git a/tests/unit/tests/unittests/CMakeLists.txt b/tests/unit/tests/unittests/CMakeLists.txt index 5de9ef141c8..7088bedcc0a 100644 --- a/tests/unit/tests/unittests/CMakeLists.txt +++ b/tests/unit/tests/unittests/CMakeLists.txt @@ -38,6 +38,7 @@ function(unittest_copy_data_folder) ) endfunction(unittest_copy_data_folder) +add_subdirectory(componentcore) add_subdirectory(listmodeleditor) add_subdirectory(imagecache) add_subdirectory(metainfo) diff --git a/tests/unit/tests/unittests/componentcore/CMakeLists.txt b/tests/unit/tests/unittests/componentcore/CMakeLists.txt new file mode 100644 index 00000000000..913fe21bea4 --- /dev/null +++ b/tests/unit/tests/unittests/componentcore/CMakeLists.txt @@ -0,0 +1,15 @@ +extend_qtc_test(unittest + SOURCES + propertycomponentgenerator-test.cpp + propertyeditorcomponentgenerator-test.cpp +) + +extend_qtc_test(unittest + SOURCES_PREFIX ../../../../../src/plugins/qmldesigner/components/componentcore + SOURCES + propertyeditorcomponentgenerator.cpp propertyeditorcomponentgenerator.h + propertycomponentgenerator.cpp propertycomponentgenerator.h + propertycomponentgeneratorinterface.h +) + + diff --git a/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp b/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp new file mode 100644 index 00000000000..353efb91f64 --- /dev/null +++ b/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp @@ -0,0 +1,322 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include +#include +#include +#include +#include +#include + +#include + +using namespace Qt::StringLiterals; + +namespace QmlDesigner { + +std::ostream &operator<<(std::ostream &out, const PropertyComponentGenerator::BasicProperty &property) +{ + return out << "BasicProperty(" << property.propertyName << ", " << property.component << ")"; +} + +std::ostream &operator<<(std::ostream &out, const PropertyComponentGenerator::ComplexProperty &property) +{ + return out << "ComplexPropertiesFromTemplates(" << property.propertyName << ", " + << property.component << ")"; +} + +} // namespace QmlDesigner + +namespace { + +using BasicProperty = QmlDesigner::PropertyComponentGenerator::BasicProperty; +using ComplexProperty = QmlDesigner::PropertyComponentGenerator::ComplexProperty; + +template +auto IsBasicProperty(const Matcher &matcher) +{ + return VariantWith(Field(&BasicProperty::component, matcher)); +} + +template +auto IsComplexProperty(const Matcher &matcher) +{ + return VariantWith(Field(&ComplexProperty::component, matcher)); +} + +constexpr Utils::SmallStringView sourcesPath = UNITTEST_DIR + "/../../../../share/qtcreator/qmldesigner/propertyEditorQmlSources"; + +class PropertyComponentGenerator : public ::testing::Test +{ +protected: + static void SetUpTestSuite() + { + simpleReaderNode = createTemplateConfiguration(QString{sourcesPath}); + } + + static void TearDownTestSuite() { simpleReaderNode.reset(); } + + static QmlJS::SimpleReaderNode::Ptr createTemplateConfiguration(const QString &propertyEditorResourcesPath) + { + QmlJS::SimpleReader reader; + const QString fileName = propertyEditorResourcesPath + u"/PropertyTemplates/TemplateTypes.qml"; + auto templateConfiguration = reader.readFile(fileName); + + if (!templateConfiguration) + qWarning().nospace() << "template definitions:" << reader.errors(); + + return templateConfiguration; + } + + template + static Type getProperty(const QmlJS::SimpleReaderNode *node, const QString &name) + { + if (auto property = node->property(name)) { + const auto &value = property.value; + if (value.type() == QVariant::List) { + auto list = value.toList(); + if (list.size()) + return list.front().value(); + } else { + return property.value.value(); + } + } + + return {}; + } + + static QString getSource(const QString &name) + { + const auto &nodes = simpleReaderNode->children(); + for (const QmlJS::SimpleReaderNode::Ptr &node : nodes) { + if (auto typeName = getProperty(node.get(), "typeNames"); typeName != name) + continue; + + auto sourcePath = getProperty(node.get(), "sourceFile"); + + return getContent(QString{sourcesPath} + "/PropertyTemplates/" + sourcePath); + } + + return {}; + } + + static QString getContent(const QString &path) + { + QFile file{path}; + + if (file.open(QIODevice::ReadOnly)) + return QString::fromUtf8(file.readAll()); + + return {}; + } + + static QString getExpectedContent(const QString &typeName, + const QString &propertyName, + const QString &binding) + { + auto source = getSource(typeName); + + return source.arg(propertyName, binding); + } + + QmlDesigner::NodeMetaInfo createTypeWithProperties(Utils::SmallStringView name, + QmlDesigner::TypeId propertyTypeId) + { + auto typeId = projectStorageMock.createValue(qmlModuleId, name); + projectStorageMock.createProperty(typeId, "sub1", propertyTypeId); + projectStorageMock.createProperty(typeId, "sub2", propertyTypeId); + + return {typeId, &projectStorageMock}; + } + + QmlDesigner::NodeMetaInfo createType(Utils::SmallStringView name) + { + auto typeId = projectStorageMock.createValue(qmlModuleId, name); + + return {typeId, &projectStorageMock}; + } + + QmlDesigner::PropertyMetaInfo createProperty(QmlDesigner::TypeId typeId, + Utils::SmallString name, + QmlDesigner::Storage::PropertyDeclarationTraits traits, + QmlDesigner::TypeId propertyTypeId) + { + auto propertyId = projectStorageMock.createProperty(typeId, name, traits, propertyTypeId); + + return {propertyId, &projectStorageMock}; + } + + QString toString(const QmlDesigner::PropertyComponentGenerator::Property &propertyComponent, + const QString &baseName, + const QString &propertyName) + { + auto text = std::visit( + [](const auto &p) -> QString { + if constexpr (!std::is_same_v, std::monostate>) + return p.component; + else + return {}; + }, + propertyComponent); + + text.replace(propertyName, baseName + "."_L1 + propertyName); + text.replace("backendValues."_L1 + baseName + "."_L1 + propertyName, + "backendValues."_L1 + baseName + "_"_L1 + propertyName); + + return text; + } + +protected: + inline static QSharedPointer simpleReaderNode; + NiceMock viewMock; + NiceMock pathCacheMock{"/path/foo.qml"}; + NiceMock projectStorageMock{pathCacheMock.sourceId}; + NiceMock resourceManagementMock; + QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, + "Item", + -1, + -1, + nullptr, + std::make_unique( + resourceManagementMock)}; + QmlDesigner::PropertyComponentGenerator generator{QString{sourcesPath}, &model}; + QmlDesigner::NodeMetaInfo itemMetaInfo = model.qtQuickItemMetaInfo(); + QmlDesigner::ModuleId qmlModuleId = projectStorageMock.createModule("QML"); +}; + +TEST_F(PropertyComponentGenerator, + basic_property_is_returned_for_property_with_a_template_with_no_separate_section) +{ + QString expected = getExpectedContent("real", "x", "x"); + auto xProperty = itemMetaInfo.property("x"); + + auto propertyComponent = generator.create(xProperty); + + ASSERT_THAT(propertyComponent, IsBasicProperty(StrippedStringEq(expected))); +} + +TEST_F(PropertyComponentGenerator, + complex_property_is_returned_for_property_with_a_template_with_a_separate_section) +{ + QString expected = getExpectedContent("font", "font", "font"); + auto fontProperty = itemMetaInfo.property("font"); + + auto propertyComponent = generator.create(fontProperty); + + ASSERT_THAT(propertyComponent, IsComplexProperty(StrippedStringEq(expected))); +} + +TEST_F(PropertyComponentGenerator, + complex_property_is_returned_for_property_without_a_template_and_subproperties) +{ + auto stringId = projectStorageMock.createValue(qmlModuleId, "string"); + auto fooNodeInfo = createTypeWithProperties("Foo", stringId); + auto sub1Text = toString(generator.create(fooNodeInfo.property("sub1")), "foo", "sub1"); + auto sub2Text = toString(generator.create(fooNodeInfo.property("sub2")), "foo", "sub2"); + auto fooProperty = createProperty(itemMetaInfo.id(), "foo", {}, fooNodeInfo.id()); + QString expectedText = QStringLiteral( + R"xy( + Section { + caption: foo - Foo + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 8 + rightPadding: 0 + expanded: false + level: 1 + SectionLayout { + )xy"); + expectedText += sub1Text; + expectedText += sub2Text; + expectedText += "}}"; + + auto propertyComponent = generator.create(fooProperty); + + ASSERT_THAT(propertyComponent, IsComplexProperty(StrippedStringEq(expectedText))); +} + +TEST_F(PropertyComponentGenerator, + pointer_readonly_complex_property_is_returned_for_property_without_a_template_and_subproperties) +{ + auto stringId = projectStorageMock.createValue(qmlModuleId, "string"); + auto fooNodeInfo = createTypeWithProperties("Foo", stringId); + auto sub1Text = toString(generator.create(fooNodeInfo.property("sub1")), "foo", "sub1"); + auto sub2Text = toString(generator.create(fooNodeInfo.property("sub2")), "foo", "sub2"); + auto fooProperty = createProperty(itemMetaInfo.id(), + "foo", + QmlDesigner::Storage::PropertyDeclarationTraits::IsPointer + | QmlDesigner::Storage::PropertyDeclarationTraits::IsReadOnly, + fooNodeInfo.id()); + QString expectedText = QStringLiteral( + R"xy( + Section { + caption: foo - Foo + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 8 + rightPadding: 0 + expanded: false + level: 1 + SectionLayout { + )xy"); + expectedText += sub1Text; + expectedText += sub2Text; + expectedText += "}}"; + + auto propertyComponent = generator.create(fooProperty); + + ASSERT_THAT(propertyComponent, IsComplexProperty(StrippedStringEq(expectedText))); +} + +TEST_F(PropertyComponentGenerator, basic_property_without_template_is_returning_monostate) +{ + auto fooNodeInfo = createType("Foo"); + auto fooProperty = createProperty(itemMetaInfo.id(), "foo", {}, fooNodeInfo.id()); + + auto propertyComponent = generator.create(fooProperty); + + ASSERT_THAT(propertyComponent, VariantWith(std::monostate{})); +} + +TEST_F(PropertyComponentGenerator, + complex_property_without_templates_and_writeable_is_returning_monostate) +{ + auto barTypeId = projectStorageMock.createValue(qmlModuleId, "bar"); + auto fooNodeInfo = createTypeWithProperties("Foo", barTypeId); + auto fooProperty = createProperty(itemMetaInfo.id(), "foo", {}, fooNodeInfo.id()); + + auto propertyComponent = generator.create(fooProperty); + + ASSERT_THAT(propertyComponent, VariantWith(std::monostate{})); +} + +TEST_F(PropertyComponentGenerator, + pointer_writeable_complex_property_without_templates_and_with_only_subproperties_without_templates_is_returning_monostate) +{ + auto stringId = projectStorageMock.createValue(qmlModuleId, "string"); + auto fooNodeInfo = createTypeWithProperties("Foo", stringId); + auto sub1Text = toString(generator.create(fooNodeInfo.property("sub1")), "foo", "sub1"); + auto sub2Text = toString(generator.create(fooNodeInfo.property("sub2")), "foo", "sub2"); + auto fooProperty = createProperty(itemMetaInfo.id(), + "foo", + QmlDesigner::Storage::PropertyDeclarationTraits::IsPointer, + fooNodeInfo.id()); + + auto propertyComponent = generator.create(fooProperty); + + ASSERT_THAT(propertyComponent, VariantWith(std::monostate{})); +} + +TEST_F(PropertyComponentGenerator, get_imports) +{ + auto imports = generator.imports(); + + ASSERT_THAT(imports, + ElementsAre(Eq("import HelperWidgets 2.0"), + Eq("import QtQuick 2.15"), + Eq("import QtQuick.Layouts 1.15"), + Eq("import StudioTheme 1.0 as StudioTheme"))); +} + +} // namespace diff --git a/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp b/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp new file mode 100644 index 00000000000..d02b2b12cbc --- /dev/null +++ b/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp @@ -0,0 +1,487 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include +#include +#include +#include + +#include + +namespace { + +using BasicProperty = QmlDesigner::PropertyComponentGenerator::BasicProperty; +using ComplexProperty = QmlDesigner::PropertyComponentGenerator::ComplexProperty; +using QmlDesigner::PropertyMetaInfo; + +class PropertyEditorTemplateGenerator : public ::testing::Test +{ +protected: + QmlDesigner::NodeMetaInfo createType(Utils::SmallStringView name, + QmlDesigner::TypeIds baseTypeIds = {}) + { + auto typeId = projectStorageMock.createValue(qtQuickModuleId, name, baseTypeIds); + + return {typeId, &projectStorageMock}; + } + + QmlDesigner::PropertyMetaInfo createProperty(QmlDesigner::TypeId typeId, + Utils::SmallString name, + QmlDesigner::Storage::PropertyDeclarationTraits traits, + QmlDesigner::TypeId propertyTypeId) + { + auto propertyId = projectStorageMock.createProperty(typeId, name, traits, propertyTypeId); + + return {propertyId, &projectStorageMock}; + } + + void setImports(const QStringList &imports) + { + ON_CALL(propertyGeneratorMock, imports()).WillByDefault(Return(imports)); + } + + void addBasicProperty(const PropertyMetaInfo &property, + Utils::SmallStringView name, + const QString &componentContent) + { + ON_CALL(propertyGeneratorMock, create(property)) + .WillByDefault(Return(BasicProperty{name, componentContent})); + } + + void addComplexProperty(const PropertyMetaInfo &property, + Utils::SmallStringView name, + const QString &componentContent) + { + ON_CALL(propertyGeneratorMock, create(property)) + .WillByDefault(Return(ComplexProperty{name, componentContent})); + } + + QmlDesigner::PropertyMetaInfo createBasicProperty(QmlDesigner::TypeId typeId, + Utils::SmallString name, + QmlDesigner::Storage::PropertyDeclarationTraits traits, + QmlDesigner::TypeId propertyTypeId, + const QString &componentContent) + { + auto propertyInfo = createProperty(typeId, name, traits, propertyTypeId); + addBasicProperty(propertyInfo, name, componentContent); + + return propertyInfo; + } + + QmlDesigner::PropertyMetaInfo createComplexProperty( + QmlDesigner::TypeId typeId, + Utils::SmallString name, + QmlDesigner::Storage::PropertyDeclarationTraits traits, + QmlDesigner::TypeId propertyTypeId, + const QString &componentContent) + { + auto propertyInfo = createProperty(typeId, name, traits, propertyTypeId); + addComplexProperty(propertyInfo, name, componentContent); + + return propertyInfo; + } + +protected: + QmlDesigner::SourceId sourceId = QmlDesigner::SourceId::create(10); + NiceMock projectStorageMock{sourceId}; + NiceMock propertyGeneratorMock; + QmlDesigner::PropertyEditorTemplateGenerator generator{propertyGeneratorMock}; + QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick"); + QmlDesigner::NodeMetaInfo fooTypeInfo = createType("Foo"); + QmlDesigner::TypeId dummyTypeId = projectStorageMock.commonTypeCache().builtinTypeId(); +}; + +TEST_F(PropertyEditorTemplateGenerator, no_properties_and_no_imports) +{ + QString expectedText{ + R"xy( + Column { + width: parent.width + Section { + caption: "Exposed Custom Properties" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + } + } + })xy"}; + + auto text = generator.create(fooTypeInfo, false); + + ASSERT_THAT(text, StrippedStringEq(expectedText)); +} + +TEST_F(PropertyEditorTemplateGenerator, properties_without_component_are_not_shows) +{ + QString expectedText{ + R"xy( + Column { + width: parent.width + Section { + caption: "Exposed Custom Properties" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + } + } + })xy"}; + createProperty(fooTypeInfo.id(), "x", {}, dummyTypeId); + + auto text = generator.create(fooTypeInfo, false); + + ASSERT_THAT(text, StrippedStringEq(expectedText)); +} + +TEST_F(PropertyEditorTemplateGenerator, show_component_button_for_a_component_node) +{ + QString expectedText{ + R"xy( + Column { + width: parent.width + ComponentButton {} + Section { + caption: "Exposed Custom Properties" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + } + } + })xy"}; + + auto text = generator.create(fooTypeInfo, true); + + ASSERT_THAT(text, StrippedStringEq(expectedText)); +} + +TEST_F(PropertyEditorTemplateGenerator, imports) +{ + QString expectedText{ + R"xy( + import QtQtuick + import Studio 2.1 + Column { + width: parent.width + Section { + caption: "Exposed Custom Properties" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + } + } + })xy"}; + setImports({"import QtQtuick", "import Studio 2.1"}); + + auto text = generator.create(fooTypeInfo, false); + + ASSERT_THAT(text, StrippedStringEq(expectedText)); +} + +TEST_F(PropertyEditorTemplateGenerator, basic_property) +{ + QString expectedText{ + R"xy( + Column { + width: parent.width + Section { + caption: "Exposed Custom Properties" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + Column { + width: parent.width + leftPadding: 8 + bottomPadding: 10 + SectionLayout { + Double{} + } + } + } + } + })xy"}; + createBasicProperty(fooTypeInfo.id(), "value", {}, dummyTypeId, "Double{}"); + + auto text = generator.create(fooTypeInfo, false); + + ASSERT_THAT(text, StrippedStringEq(expectedText)); +} + +TEST_F(PropertyEditorTemplateGenerator, basic_properties_with_base_type) +{ + QString expectedText{ + R"xy( + Column { + width: parent.width + Section { + caption: "Exposed Custom Properties" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + Column { + width: parent.width + leftPadding: 8 + bottomPadding: 10 + SectionLayout { + SuperDouble{} + Double{} + } + } + } + } + })xy"}; + createBasicProperty(fooTypeInfo.id(), "x", {}, dummyTypeId, "Double{}"); + auto superFooInfo = createType("SuperFoo", {fooTypeInfo.id()}); + createBasicProperty(superFooInfo.id(), "value", {}, dummyTypeId, "SuperDouble{}"); + + auto text = generator.create(superFooInfo, false); + + ASSERT_THAT(text, StrippedStringEq(expectedText)); +} + +TEST_F(PropertyEditorTemplateGenerator, + only_handle_basic_properties_for_types_without_specifics_or_panes) +{ + QString expectedText{ + R"xy( + Column { + width: parent.width + Section { + caption: "Exposed Custom Properties" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + Column { + width: parent.width + leftPadding: 8 + bottomPadding: 10 + SectionLayout { + SuperDouble{} + } + } + } + } + })xy"}; + createBasicProperty(fooTypeInfo.id(), "x", {}, dummyTypeId, "Double{}"); + auto superFooInfo = createType("SuperFoo", {fooTypeInfo.id()}); + createBasicProperty(superFooInfo.id(), "value", {}, dummyTypeId, "SuperDouble{}"); + projectStorageMock.setPropertyEditorPathId(fooTypeInfo.id(), sourceId); + + auto text = generator.create(superFooInfo, false); + + ASSERT_THAT(text, StrippedStringEq(expectedText)); +} + +TEST_F(PropertyEditorTemplateGenerator, order_basic_property) +{ + QString expectedText{ + R"xy( + Column { + width: parent.width + Section { + caption: "Exposed Custom Properties" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + Column { + width: parent.width + leftPadding: 8 + bottomPadding: 10 + SectionLayout { + AnotherX{} + AndY{} + SomeZ{} + } + } + } + } + })xy"}; + createBasicProperty(fooTypeInfo.id(), "z", {}, dummyTypeId, "SomeZ{}"); + createBasicProperty(fooTypeInfo.id(), "x", {}, dummyTypeId, "AnotherX{}"); + createBasicProperty(fooTypeInfo.id(), "y", {}, dummyTypeId, "AndY{}"); + + auto text = generator.create(fooTypeInfo, false); + + ASSERT_THAT(text, StrippedStringEq(expectedText)); +} + +TEST_F(PropertyEditorTemplateGenerator, complex_property) +{ + QString expectedText{ + R"xy( + Column { + width: parent.width + Section { + caption: "Exposed Custom Properties" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + Complex{} + } + } + })xy"}; + createComplexProperty(fooTypeInfo.id(), "value", {}, dummyTypeId, "Complex{}"); + + auto text = generator.create(fooTypeInfo, false); + + ASSERT_THAT(text, StrippedStringEq(expectedText)); +} + +TEST_F(PropertyEditorTemplateGenerator, complex_properties_with_base_type) +{ + QString expectedText{ + R"xy( + Column { + width: parent.width + Section { + caption: "Exposed Custom Properties" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + Complex{} + SuperComplex{} + } + } + })xy"}; + createComplexProperty(fooTypeInfo.id(), "delegate", {}, dummyTypeId, "Complex{}"); + auto superFooInfo = createType("SuperFoo", {fooTypeInfo.id()}); + createComplexProperty(superFooInfo.id(), "value", {}, dummyTypeId, "SuperComplex{}"); + + auto text = generator.create(superFooInfo, false); + + ASSERT_THAT(text, StrippedStringEq(expectedText)); +} + +TEST_F(PropertyEditorTemplateGenerator, + only_handle_complex_properties_for_types_without_specifics_or_panes) +{ + QString expectedText{ + R"xy( + Column { + width: parent.width + Section { + caption: "Exposed Custom Properties" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + SuperComplex{} + } + } + })xy"}; + createComplexProperty(fooTypeInfo.id(), "delegate", {}, dummyTypeId, "Complex{}"); + auto superFooInfo = createType("SuperFoo", {fooTypeInfo.id()}); + createComplexProperty(superFooInfo.id(), "value", {}, dummyTypeId, "SuperComplex{}"); + projectStorageMock.setPropertyEditorPathId(fooTypeInfo.id(), sourceId); + + auto text = generator.create(superFooInfo, false); + + ASSERT_THAT(text, StrippedStringEq(expectedText)); +} + +TEST_F(PropertyEditorTemplateGenerator, ordered_complex_property) +{ + QString expectedText{ + R"xy( + Column { + width: parent.width + Section { + caption: "Exposed Custom Properties" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + Anchors{} + Delegate{} + ComplexFont{} + } + } + })xy"}; + createComplexProperty(fooTypeInfo.id(), "delegate", {}, dummyTypeId, "Delegate{}"); + createComplexProperty(fooTypeInfo.id(), "font", {}, dummyTypeId, "ComplexFont{}"); + createComplexProperty(fooTypeInfo.id(), "anchors", {}, dummyTypeId, "Anchors{}"); + + auto text = generator.create(fooTypeInfo, false); + + ASSERT_THAT(text, StrippedStringEq(expectedText)); +} + +TEST_F(PropertyEditorTemplateGenerator, basic_is_placed_before_complex_components) +{ + QString expectedText{ + R"xy( + Column { + width: parent.width + Section { + caption: "Exposed Custom Properties" + anchors.left: parent.left + anchors.right: parent.right + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + Column { + width: parent.width + Column { + width: parent.width + leftPadding: 8 + bottomPadding: 10 + SectionLayout { + Double{} + } + } + Font{} + } + } + })xy"}; + createBasicProperty(fooTypeInfo.id(), "x", {}, dummyTypeId, "Double{}"); + createComplexProperty(fooTypeInfo.id(), "font", {}, dummyTypeId, "Font{}"); + + auto text = generator.create(fooTypeInfo, false); + + ASSERT_THAT(text, StrippedStringEq(expectedText)); +} +} // namespace diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp index c1f3b4f8d7c..c21d3259365 100644 --- a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp @@ -409,7 +409,7 @@ TEST_F(NodeMetaInfo, get_invalid_property_if_not_exists) { auto metaInfo = model.qtQuickItemMetaInfo(); - auto property = metaInfo.property("x"); + auto property = metaInfo.property("foo"); ASSERT_THAT(property, PropertyId(IsFalse())); } diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index 17374d4314c..b705c15ffad 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -869,6 +869,49 @@ TEST_F(Model, meta_info_of_not_existing_type_is_invalid) ASSERT_THAT(meta_info, IsFalse()); } +TEST_F(Model, module_is_valid) +{ + auto module = model.module("QML"); + + ASSERT_THAT(module, IsTrue()); +} + +TEST_F(Model, module_returns_always_the_same) +{ + auto oldModule = model.module("QML"); + + auto module = model.module("QML"); + + ASSERT_THAT(module, oldModule); +} + +TEST_F(Model, get_meta_info_by_module) +{ + auto module = model.module("QML"); + + auto metaInfo = model.metaInfo(module, "QtObject"); + + ASSERT_THAT(metaInfo, model.qmlQtObjectMetaInfo()); +} + +TEST_F(Model, get_invalid_meta_info_by_module_for_wrong_name) +{ + auto module = model.module("QML"); + + auto metaInfo = model.metaInfo(module, "Object"); + + ASSERT_THAT(metaInfo, IsFalse()); +} + +TEST_F(Model, get_invalid_meta_info_by_module_for_wrong_module) +{ + auto module = model.module("Qml"); + + auto metaInfo = model.metaInfo(module, "Object"); + + ASSERT_THAT(metaInfo, IsFalse()); +} + TEST_F(Model, add_refresh_callback_to_project_storage) { EXPECT_CALL(projectStorageMock, addRefreshCallback(_)); From 5aea0da74280ac86ce5323168b4cb8b84a7bd794 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 29 Sep 2023 10:39:40 +0200 Subject: [PATCH 114/130] Sqlite: Update to 3.43.1 Change-Id: Ife5e6be8d88dec33cc7bfb236006468bb3a49ab0 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/3rdparty/sqlite/sqlite3.c | 7881 +++++++++++++++++-------- src/libs/3rdparty/sqlite/sqlite3.h | 106 +- src/libs/3rdparty/sqlite/sqlite3ext.h | 4 + 3 files changed, 5613 insertions(+), 2378 deletions(-) diff --git a/src/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c index dd3b5c57570..1884b082396 100644 --- a/src/libs/3rdparty/sqlite/sqlite3.c +++ b/src/libs/3rdparty/sqlite/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.42.0. By combining all the individual C code files into this +** version 3.43.1. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -16,6 +16,9 @@ ** if you want a wrapper to interface SQLite with your choice of programming ** language. The code for the "sqlite3" command-line shell is also in a ** separate file. This file contains only code for the core SQLite library. +** +** The content in this amalgamation comes from Fossil check-in +** d3a40c05c49e1a49264912b1a05bc2143ac. */ #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 @@ -50,11 +53,11 @@ ** used on lines of code that actually ** implement parts of coverage testing. ** -** OPTIMIZATION-IF-TRUE - This branch is allowed to alway be false +** OPTIMIZATION-IF-TRUE - This branch is allowed to always be false ** and the correct answer is still obtained, ** though perhaps more slowly. ** -** OPTIMIZATION-IF-FALSE - This branch is allowed to alway be true +** OPTIMIZATION-IF-FALSE - This branch is allowed to always be true ** and the correct answer is still obtained, ** though perhaps more slowly. ** @@ -456,9 +459,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.42.0" -#define SQLITE_VERSION_NUMBER 3042000 -#define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0" +#define SQLITE_VERSION "3.43.1" +#define SQLITE_VERSION_NUMBER 3043001 +#define SQLITE_SOURCE_ID "2023-09-11 12:01:27 2d3a40c05c49e1a49264912b1a05bc2143ac0e7c3df588276ce80a4cbc9bd1b0" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -838,6 +841,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) +#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) @@ -1500,7 +1504,7 @@ struct sqlite3_io_methods { ** by clients within the current process, only within other processes. ** **
  • [[SQLITE_FCNTL_CKSM_FILE]] -** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use interally by the +** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use internally by the ** [checksum VFS shim] only. ** **
  • [[SQLITE_FCNTL_RESET_CACHE]] @@ -2764,7 +2768,7 @@ struct sqlite3_mem_methods { ** the [VACUUM] command will fail with an obscure error when attempting to ** process a table with generated columns and a descending index. This is ** not considered a bug since SQLite versions 3.3.0 and earlier do not support -** either generated columns or decending indexes. +** either generated columns or descending indexes. ** ** ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] @@ -3045,6 +3049,7 @@ SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*); ** ** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether ** or not an interrupt is currently in effect for [database connection] D. +** It returns 1 if an interrupt is currently in effect, or 0 otherwise. */ SQLITE_API void sqlite3_interrupt(sqlite3*); SQLITE_API int sqlite3_is_interrupted(sqlite3*); @@ -3698,8 +3703,10 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, ** M argument should be the bitwise OR-ed combination of ** zero or more [SQLITE_TRACE] constants. ** -** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides -** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2(). +** ^Each call to either sqlite3_trace(D,X,P) or sqlite3_trace_v2(D,M,X,P) +** overrides (cancels) all prior calls to sqlite3_trace(D,X,P) or +** sqlite3_trace_v2(D,M,X,P) for the [database connection] D. Each +** database connection may have at most one trace callback. ** ** ^The X callback is invoked whenever any of the events identified by ** mask M occur. ^The integer return value from the callback is currently @@ -4068,7 +4075,7 @@ SQLITE_API int sqlite3_open_v2( ** as F) must be one of: **
      **
    • A database filename pointer created by the SQLite core and -** passed into the xOpen() method of a VFS implemention, or +** passed into the xOpen() method of a VFS implementation, or **
    • A filename obtained from [sqlite3_db_filename()], or **
    • A new filename constructed using [sqlite3_create_filename()]. **
    @@ -4181,7 +4188,7 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); /* ** CAPI3REF: Create and Destroy VFS Filenames ** -** These interfces are provided for use by [VFS shim] implementations and +** These interfaces are provided for use by [VFS shim] implementations and ** are not useful outside of that context. ** ** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of @@ -4728,6 +4735,41 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); */ SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); +/* +** CAPI3REF: Change The EXPLAIN Setting For A Prepared Statement +** METHOD: sqlite3_stmt +** +** The sqlite3_stmt_explain(S,E) interface changes the EXPLAIN +** setting for [prepared statement] S. If E is zero, then S becomes +** a normal prepared statement. If E is 1, then S behaves as if +** its SQL text began with "[EXPLAIN]". If E is 2, then S behaves as if +** its SQL text began with "[EXPLAIN QUERY PLAN]". +** +** Calling sqlite3_stmt_explain(S,E) might cause S to be reprepared. +** SQLite tries to avoid a reprepare, but a reprepare might be necessary +** on the first transition into EXPLAIN or EXPLAIN QUERY PLAN mode. +** +** Because of the potential need to reprepare, a call to +** sqlite3_stmt_explain(S,E) will fail with SQLITE_ERROR if S cannot be +** reprepared because it was created using [sqlite3_prepare()] instead of +** the newer [sqlite3_prepare_v2()] or [sqlite3_prepare_v3()] interfaces and +** hence has no saved SQL text with which to reprepare. +** +** Changing the explain setting for a prepared statement does not change +** the original SQL text for the statement. Hence, if the SQL text originally +** began with EXPLAIN or EXPLAIN QUERY PLAN, but sqlite3_stmt_explain(S,0) +** is called to convert the statement into an ordinary statement, the EXPLAIN +** or EXPLAIN QUERY PLAN keywords will still appear in the sqlite3_sql(S) +** output, even though the statement now acts like a normal SQL statement. +** +** This routine returns SQLITE_OK if the explain mode is successfully +** changed, or an error code if the explain mode could not be changed. +** The explain mode cannot be changed while a statement is active. +** Hence, it is good practice to call [sqlite3_reset(S)] +** immediately prior to calling sqlite3_stmt_explain(S,E). +*/ +SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode); + /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt @@ -4891,7 +4933,7 @@ typedef struct sqlite3_context sqlite3_context; ** with it may be passed. ^It is called to dispose of the BLOB or string even ** if the call to the bind API fails, except the destructor is not called if ** the third parameter is a NULL pointer or the fourth parameter is negative. -** ^ (2) The special constant, [SQLITE_STATIC], may be passsed to indicate that +** ^ (2) The special constant, [SQLITE_STATIC], may be passed to indicate that ** the application remains responsible for disposing of the object. ^In this ** case, the object and the provided pointer to it must remain valid until ** either the prepared statement is finalized or the same SQL parameter is @@ -5570,14 +5612,26 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); ** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S ** back to the beginning of its program. ** -** ^If the most recent call to [sqlite3_step(S)] for the -** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], -** or if [sqlite3_step(S)] has never before been called on S, -** then [sqlite3_reset(S)] returns [SQLITE_OK]. +** ^The return code from [sqlite3_reset(S)] indicates whether or not +** the previous evaluation of prepared statement S completed successfully. +** ^If [sqlite3_step(S)] has never before been called on S or if +** [sqlite3_step(S)] has not been called since the previous call +** to [sqlite3_reset(S)], then [sqlite3_reset(S)] will return +** [SQLITE_OK]. ** ** ^If the most recent call to [sqlite3_step(S)] for the ** [prepared statement] S indicated an error, then ** [sqlite3_reset(S)] returns an appropriate [error code]. +** ^The [sqlite3_reset(S)] interface might also return an [error code] +** if there were no prior errors but the process of resetting +** the prepared statement caused a new error. ^For example, if an +** [INSERT] statement with a [RETURNING] clause is only stepped one time, +** that one call to [sqlite3_step(S)] might return SQLITE_ROW but +** the overall statement might still fail and the [sqlite3_reset(S)] call +** might return SQLITE_BUSY if locking constraints prevent the +** database change from committing. Therefore, it is important that +** applications check the return code from [sqlite3_reset(S)] even if +** no prior call to [sqlite3_step(S)] indicated a problem. ** ** ^The [sqlite3_reset(S)] interface does not change the values ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. @@ -5794,7 +5848,7 @@ SQLITE_API int sqlite3_create_window_function( ** [application-defined SQL function] ** that has side-effects or that could potentially leak sensitive information. ** This will prevent attacks in which an application is tricked -** into using a database file that has had its schema surreptiously +** into using a database file that has had its schema surreptitiously ** modified to invoke the application-defined function in ways that are ** harmful. **

    @@ -8471,7 +8525,8 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_TRACEFLAGS 31 #define SQLITE_TESTCTRL_TUNE 32 #define SQLITE_TESTCTRL_LOGEST 33 -#define SQLITE_TESTCTRL_LAST 33 /* Largest TESTCTRL */ +#define SQLITE_TESTCTRL_USELONGDOUBLE 34 +#define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking @@ -9503,8 +9558,8 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing -** unlock-notify callback is canceled. ^The blocked connections -** unlock-notify callback may also be canceled by closing the blocked +** unlock-notify callback is cancelled. ^The blocked connections +** unlock-notify callback may also be cancelled by closing the blocked ** connection using [sqlite3_close()]. ** ** The unlock-notify callback is not reentrant. If an application invokes @@ -9927,7 +9982,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); ** [[SQLITE_VTAB_DIRECTONLY]]

    SQLITE_VTAB_DIRECTONLY
    **
    Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_DIRECTONLY) from within the -** the [xConnect] or [xCreate] methods of a [virtual table] implmentation +** the [xConnect] or [xCreate] methods of a [virtual table] implementation ** prohibits that virtual table from being used from within triggers and ** views. **
    @@ -10117,7 +10172,7 @@ SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*); ** communicated to the xBestIndex method as a ** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use ** this constraint, it must set the corresponding -** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under +** aConstraintUsage[].argvIndex to a positive integer. ^(Then, under ** the usual mode of handling IN operators, SQLite generates [bytecode] ** that invokes the [xFilter|xFilter() method] once for each value ** on the right-hand side of the IN operator.)^ Thus the virtual table @@ -10546,7 +10601,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); ** When the [sqlite3_blob_write()] API is used to update a blob column, ** the pre-update hook is invoked with SQLITE_DELETE. This is because the ** in this case the new values are not available. In this case, when a -** callback made with op==SQLITE_DELETE is actuall a write using the +** callback made with op==SQLITE_DELETE is actually a write using the ** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns ** the index of the column being written. In other cases, where the ** pre-update hook is being invoked for some other reason, including a @@ -13064,7 +13119,7 @@ struct Fts5PhraseIter { ** See xPhraseFirstColumn above. */ struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 3 */ + int iVersion; /* Currently always set to 2 */ void *(*xUserData)(Fts5Context*); @@ -13293,8 +13348,8 @@ struct Fts5ExtensionApi { ** as separate queries of the FTS index are required for each synonym. ** ** When using methods (2) or (3), it is important that the tokenizer only -** provide synonyms when tokenizing document text (method (2)) or query -** text (method (3)), not both. Doing so will not cause any errors, but is +** provide synonyms when tokenizing document text (method (3)) or query +** text (method (2)), not both. Doing so will not cause any errors, but is ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; @@ -13342,7 +13397,7 @@ struct fts5_api { int (*xCreateTokenizer)( fts5_api *pApi, const char *zName, - void *pContext, + void *pUserData, fts5_tokenizer *pTokenizer, void (*xDestroy)(void*) ); @@ -13351,7 +13406,7 @@ struct fts5_api { int (*xFindTokenizer)( fts5_api *pApi, const char *zName, - void **ppContext, + void **ppUserData, fts5_tokenizer *pTokenizer ); @@ -13359,7 +13414,7 @@ struct fts5_api { int (*xCreateFunction)( fts5_api *pApi, const char *zName, - void *pContext, + void *pUserData, fts5_extension_function xFunction, void (*xDestroy)(void*) ); @@ -13470,7 +13525,7 @@ struct fts5_api { ** level of recursion for each term. A stack overflow can result ** if the number of terms is too large. In practice, most SQL ** never has more than 3 or 4 terms. Use a value of 0 to disable -** any limit on the number of terms in a compount SELECT. +** any limit on the number of terms in a compound SELECT. */ #ifndef SQLITE_MAX_COMPOUND_SELECT # define SQLITE_MAX_COMPOUND_SELECT 500 @@ -14573,8 +14628,31 @@ typedef INT16_TYPE LogEst; ** the end of buffer S. This macro returns true if P points to something ** contained within the buffer S. */ -#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E))) +#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E))) +/* +** P is one byte past the end of a large buffer. Return true if a span of bytes +** between S..E crosses the end of that buffer. In other words, return true +** if the sub-buffer S..E-1 overflows the buffer whose last byte is P-1. +** +** S is the start of the span. E is one byte past the end of end of span. +** +** P +** |-----------------| FALSE +** |-------| +** S E +** +** P +** |-----------------| +** |-------| TRUE +** S E +** +** P +** |-----------------| +** |-------| FALSE +** S E +*/ +#define SQLITE_OVERFLOW(P,S,E) (((uptr)(S)<(uptr)(P))&&((uptr)(E)>(uptr)(P))) /* ** Macros to determine whether the machine is big or little endian, @@ -14808,7 +14886,7 @@ struct BusyHandler { /* ** Name of table that holds the database schema. ** -** The PREFERRED names are used whereever possible. But LEGACY is also +** The PREFERRED names are used wherever possible. But LEGACY is also ** used for backwards compatibility. ** ** 1. Queries can use either the PREFERRED or the LEGACY names @@ -14922,6 +15000,7 @@ typedef struct Schema Schema; typedef struct Expr Expr; typedef struct ExprList ExprList; typedef struct FKey FKey; +typedef struct FpDecode FpDecode; typedef struct FuncDestructor FuncDestructor; typedef struct FuncDef FuncDef; typedef struct FuncDefHash FuncDefHash; @@ -14940,6 +15019,7 @@ typedef struct Parse Parse; typedef struct ParseCleanup ParseCleanup; typedef struct PreUpdate PreUpdate; typedef struct PrintfArguments PrintfArguments; +typedef struct RCStr RCStr; typedef struct RenameToken RenameToken; typedef struct Returning Returning; typedef struct RowSet RowSet; @@ -15577,6 +15657,10 @@ SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); # define enable_simulated_io_errors() #endif +#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL) +SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager*); +#endif + #endif /* SQLITE_PAGER_H */ /************** End of pager.h ***********************************************/ @@ -15906,9 +15990,7 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int flags); SQLITE_PRIVATE i64 sqlite3BtreeIntegerKey(BtCursor*); SQLITE_PRIVATE void sqlite3BtreeCursorPin(BtCursor*); SQLITE_PRIVATE void sqlite3BtreeCursorUnpin(BtCursor*); -#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor*); -#endif SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*); SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*); @@ -16383,7 +16465,7 @@ typedef struct VdbeOpList VdbeOpList; /* 8 */ 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x01, 0x01,\ /* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0x49, 0x49, 0x49,\ /* 24 */ 0x49, 0x01, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,\ -/* 32 */ 0x41, 0x01, 0x01, 0x01, 0x41, 0x01, 0x41, 0x41,\ +/* 32 */ 0x41, 0x01, 0x41, 0x41, 0x41, 0x01, 0x41, 0x41,\ /* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x23, 0x0b,\ /* 48 */ 0x01, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ /* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x03, 0x01, 0x41,\ @@ -16395,7 +16477,7 @@ typedef struct VdbeOpList VdbeOpList; /* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\ /* 112 */ 0x40, 0x00, 0x12, 0x40, 0x40, 0x10, 0x40, 0x00,\ /* 120 */ 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10, 0x10,\ -/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,\ +/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x50,\ /* 136 */ 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50, 0x40,\ /* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ /* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\ @@ -16577,7 +16659,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); ** The VdbeCoverage macros are used to set a coverage testing point ** for VDBE branch instructions. The coverage testing points are line ** numbers in the sqlite3.c source file. VDBE branch coverage testing -** only works with an amalagmation build. That's ok since a VDBE branch +** only works with an amalgamation build. That's ok since a VDBE branch ** coverage build designed for testing the test suite only. No application ** should ever ship with VDBE branch coverage measuring turned on. ** @@ -16595,7 +16677,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); ** // NULL option is not possible ** ** VdbeCoverageEqNe(v) // Previous OP_Jump is only interested -** // in distingishing equal and not-equal. +** // in distinguishing equal and not-equal. ** ** Every VDBE branch operation must be tagged with one of the macros above. ** If not, then when "make test" is run with -DSQLITE_VDBE_COVERAGE and @@ -16605,7 +16687,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); ** During testing, the test application will invoke ** sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE,...) to set a callback ** routine that is invoked as each bytecode branch is taken. The callback -** contains the sqlite3.c source line number ov the VdbeCoverage macro and +** contains the sqlite3.c source line number of the VdbeCoverage macro and ** flags to indicate whether or not the branch was taken. The test application ** is responsible for keeping track of this and reporting byte-code branches ** that are never taken. @@ -16944,7 +17026,7 @@ SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); /* ** Default synchronous levels. ** -** Note that (for historcal reasons) the PAGER_SYNCHRONOUS_* macros differ +** Note that (for historical reasons) the PAGER_SYNCHRONOUS_* macros differ ** from the SQLITE_DEFAULT_SYNCHRONOUS value by 1. ** ** PAGER_SYNCHRONOUS DEFAULT_SYNCHRONOUS @@ -16983,7 +17065,7 @@ struct Db { ** An instance of the following structure stores a database schema. ** ** Most Schema objects are associated with a Btree. The exception is -** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing. +** the Schema for the TEMP database (sqlite3.aDb[1]) which is free-standing. ** In shared cache mode, a single Schema object can be shared by multiple ** Btrees that refer to the same underlying BtShared object. ** @@ -17094,7 +17176,7 @@ struct Lookaside { LookasideSlot *pInit; /* List of buffers not previously used */ LookasideSlot *pFree; /* List of available buffers */ #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - LookasideSlot *pSmallInit; /* List of small buffers not prediously used */ + LookasideSlot *pSmallInit; /* List of small buffers not previously used */ LookasideSlot *pSmallFree; /* List of available small buffers */ void *pMiddle; /* First byte past end of full-size buffers and ** the first byte of LOOKASIDE_SMALL buffers */ @@ -17111,7 +17193,7 @@ struct LookasideSlot { #define EnableLookaside db->lookaside.bDisable--;\ db->lookaside.sz=db->lookaside.bDisable?0:db->lookaside.szTrue -/* Size of the smaller allocations in two-size lookside */ +/* Size of the smaller allocations in two-size lookaside */ #ifdef SQLITE_OMIT_TWOSIZE_LOOKASIDE # define LOOKASIDE_SMALL 0 #else @@ -17450,6 +17532,7 @@ struct sqlite3 { #define SQLITE_IndexedExpr 0x01000000 /* Pull exprs from index when able */ #define SQLITE_Coroutines 0x02000000 /* Co-routines for subqueries */ #define SQLITE_NullUnusedCols 0x04000000 /* NULL unused columns in subqueries */ +#define SQLITE_OnePass 0x08000000 /* Single-pass DELETE and UPDATE */ #define SQLITE_AllOpts 0xffffffff /* All optimizations */ /* @@ -17532,6 +17615,7 @@ struct FuncDestructor { ** SQLITE_FUNC_ANYORDER == NC_OrderAgg == SF_OrderByReqd ** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG ** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG +** SQLITE_FUNC_BYTELEN == OPFLAG_BYTELENARG ** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API ** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API ** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS -- opposite meanings!!! @@ -17539,7 +17623,7 @@ struct FuncDestructor { ** ** Note that even though SQLITE_FUNC_UNSAFE and SQLITE_INNOCUOUS have the ** same bit value, their meanings are inverted. SQLITE_FUNC_UNSAFE is -** used internally and if set means tha the function has side effects. +** used internally and if set means that the function has side effects. ** SQLITE_INNOCUOUS is used by application code and means "not unsafe". ** See multiple instances of tag-20230109-1. */ @@ -17550,6 +17634,7 @@ struct FuncDestructor { #define SQLITE_FUNC_NEEDCOLL 0x0020 /* sqlite3GetFuncCollSeq() might be called*/ #define SQLITE_FUNC_LENGTH 0x0040 /* Built-in length() function */ #define SQLITE_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */ +#define SQLITE_FUNC_BYTELEN 0x00c0 /* Built-in octet_length() function */ #define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */ /* 0x0200 -- available for reuse */ #define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */ @@ -18129,7 +18214,7 @@ struct FKey { ** foreign key. ** ** The OE_Default value is a place holder that means to use whatever -** conflict resolution algorthm is required from context. +** conflict resolution algorithm is required from context. ** ** The following symbolic values are used to record which type ** of conflict resolution action to take. @@ -18543,7 +18628,7 @@ struct Expr { ** TK_REGISTER: register number ** TK_TRIGGER: 1 -> new, 0 -> old ** EP_Unlikely: 134217728 times likelihood - ** TK_IN: ephemerial table holding RHS + ** TK_IN: ephemeral table holding RHS ** TK_SELECT_COLUMN: Number of columns on the LHS ** TK_SELECT: 1st register of result vector */ ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid. @@ -18625,6 +18710,8 @@ struct Expr { */ #define ExprUseUToken(E) (((E)->flags&EP_IntValue)==0) #define ExprUseUValue(E) (((E)->flags&EP_IntValue)!=0) +#define ExprUseWOfst(E) (((E)->flags&(EP_InnerON|EP_OuterON))==0) +#define ExprUseWJoin(E) (((E)->flags&(EP_InnerON|EP_OuterON))!=0) #define ExprUseXList(E) (((E)->flags&EP_xIsSelect)==0) #define ExprUseXSelect(E) (((E)->flags&EP_xIsSelect)!=0) #define ExprUseYTab(E) (((E)->flags&(EP_WinFunc|EP_Subrtn))==0) @@ -18813,7 +18900,7 @@ struct SrcItem { unsigned notCte :1; /* This item may not match a CTE */ unsigned isUsing :1; /* u3.pUsing is valid */ unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ - unsigned isSynthUsing :1; /* u3.pUsing is synthensized from NATURAL */ + unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */ unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ } fg; int iCursor; /* The VDBE cursor number used to access this table */ @@ -19349,6 +19436,9 @@ struct Parse { int regRoot; /* Register holding root page number for new objects */ int nMaxArg; /* Max args passed to user function by sub-program */ int nSelect; /* Number of SELECT stmts. Counter for Select.selId */ +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + u32 nProgressSteps; /* xProgress steps taken during sqlite3_prepare() */ +#endif #ifndef SQLITE_OMIT_SHARED_CACHE int nTableLock; /* Number of locks in aTableLock */ TableLock *aTableLock; /* Required table locks for shared-cache mode */ @@ -19362,12 +19452,9 @@ struct Parse { int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */ Returning *pReturning; /* The RETURNING clause */ } u1; - u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u32 oldmask; /* Mask of old.* columns referenced */ u32 newmask; /* Mask of new.* columns referenced */ -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - u32 nProgressSteps; /* xProgress steps taken during sqlite3_prepare() */ -#endif + LogEst nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ u8 bReturning; /* Coding a RETURNING trigger */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ @@ -19491,6 +19578,7 @@ struct AuthContext { #define OPFLAG_ISNOOP 0x40 /* OP_Delete does pre-update-hook only */ #define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */ #define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */ +#define OPFLAG_BYTELENARG 0xc0 /* OP_Column only for octet_length() */ #define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */ #define OPFLAG_SEEKEQ 0x02 /* OP_Open** cursor uses EQ seek only */ #define OPFLAG_FORDELETE 0x08 /* OP_Open should use BTREE_FORDELETE */ @@ -19633,6 +19721,25 @@ struct sqlite3_str { #define isMalloced(X) (((X)->printfFlags & SQLITE_PRINTF_MALLOCED)!=0) +/* +** The following object is the header for an "RCStr" or "reference-counted +** string". An RCStr is passed around and used like any other char* +** that has been dynamically allocated. The important interface +** differences: +** +** 1. RCStr strings are reference counted. They are deallocated +** when the reference count reaches zero. +** +** 2. Use sqlite3RCStrUnref() to free an RCStr string rather than +** sqlite3_free() +** +** 3. Make a (read-only) copy of a read-only RCStr string using +** sqlite3RCStrRef(). +*/ +struct RCStr { + u64 nRCRef; /* Number of references */ + /* Total structure size should be a multiple of 8 bytes for alignment */ +}; /* ** A pointer to this structure is used to communicate information @@ -19659,7 +19766,7 @@ typedef struct { /* Tuning parameters are set using SQLITE_TESTCTRL_TUNE and are controlled ** on debug-builds of the CLI using ".testctrl tune ID VALUE". Tuning ** parameters are for temporary use during development, to help find -** optimial values for parameters in the query planner. The should not +** optimal values for parameters in the query planner. The should not ** be used on trunk check-ins. They are a temporary mechanism available ** for transient development builds only. ** @@ -19685,6 +19792,7 @@ struct Sqlite3Config { u8 bUseCis; /* Use covering indices for full-scans */ u8 bSmallMalloc; /* Avoid large memory allocations if true */ u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ + u8 bUseLongDouble; /* Make use of long double */ int mxStrlen; /* Maximum string length */ int neverCorrupt; /* Database is always well-formed */ int szLookaside; /* Default lookaside buffer size */ @@ -19771,6 +19879,7 @@ struct Walker { void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */ int walkerDepth; /* Number of subqueries */ u16 eCode; /* A small processing code */ + u16 mWFlags; /* Use-dependent flags */ union { /* Extra data for callback */ NameContext *pNC; /* Naming context */ int n; /* A counter */ @@ -19810,6 +19919,7 @@ struct DbFixer { /* Forward declarations */ SQLITE_PRIVATE int sqlite3WalkExpr(Walker*, Expr*); +SQLITE_PRIVATE int sqlite3WalkExprNN(Walker*, Expr*); SQLITE_PRIVATE int sqlite3WalkExprList(Walker*, ExprList*); SQLITE_PRIVATE int sqlite3WalkSelect(Walker*, Select*); SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker*, Select*); @@ -20191,6 +20301,20 @@ struct PrintfArguments { sqlite3_value **apArg; /* The argument values */ }; +/* +** An instance of this object receives the decoding of a floating point +** value into an approximate decimal representation. +*/ +struct FpDecode { + char sign; /* '+' or '-' */ + char isSpecial; /* 1: Infinity 2: NaN */ + int n; /* Significant digits in the decode */ + int iDP; /* Location of the decimal point */ + char *z; /* Start of significant digits */ + char zBuf[24]; /* Storage for significant digits */ +}; + +SQLITE_PRIVATE void sqlite3FpDecode(FpDecode*,double,int,int); SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3*,const char*, ...); SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3*,const char*, va_list); #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) @@ -20481,7 +20605,7 @@ SQLITE_PRIVATE int sqlite3ExprCompare(const Parse*,const Expr*,const Expr*, int) SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr*,Expr*,int); SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList*,const ExprList*, int); SQLITE_PRIVATE int sqlite3ExprImpliesExpr(const Parse*,const Expr*,const Expr*, int); -SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr*,int); +SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr*,int,int); SQLITE_PRIVATE void sqlite3AggInfoPersistWalkerInit(Walker*,Parse*); SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); @@ -20630,6 +20754,7 @@ SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); + SQLITE_PRIVATE int sqlite3RealSameAsInt(double,sqlite3_int64); SQLITE_PRIVATE i64 sqlite3RealToI64(double); SQLITE_PRIVATE int sqlite3Int64ToText(i64,char*); @@ -20734,6 +20859,7 @@ SQLITE_PRIVATE void sqlite3FileSuffix3(const char*, char*); SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z,u8); SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value*, u8); +SQLITE_PRIVATE int sqlite3ValueIsOfClass(const sqlite3_value*, void(*)(void*)); SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value*, u8); SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); @@ -20841,6 +20967,11 @@ SQLITE_PRIVATE void sqlite3OomClear(sqlite3*); SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int); SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *); +SQLITE_PRIVATE char *sqlite3RCStrRef(char*); +SQLITE_PRIVATE void sqlite3RCStrUnref(char*); +SQLITE_PRIVATE char *sqlite3RCStrNew(u64); +SQLITE_PRIVATE char *sqlite3RCStrResize(char*,u64); + SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum*, i64); SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*); @@ -21092,6 +21223,7 @@ SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse*, int); #define sqlite3SelectExprHeight(x) 0 #define sqlite3ExprCheckHeight(x,y) #endif +SQLITE_PRIVATE void sqlite3ExprSetErrorOffset(Expr*,int); SQLITE_PRIVATE u32 sqlite3Get4byte(const u8*); SQLITE_PRIVATE void sqlite3Put4byte(u8*, u32); @@ -21377,9 +21509,6 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_4_BYTE_ALIGNED_MALLOC "4_BYTE_ALIGNED_MALLOC", #endif -#ifdef SQLITE_64BIT_STATS - "64BIT_STATS", -#endif #ifdef SQLITE_ALLOW_COVERING_INDEX_SCAN # if SQLITE_ALLOW_COVERING_INDEX_SCAN != 1 "ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN), @@ -21716,6 +21845,9 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_INTEGRITY_CHECK_ERROR_MAX "INTEGRITY_CHECK_ERROR_MAX=" CTIMEOPT_VAL(SQLITE_INTEGRITY_CHECK_ERROR_MAX), #endif +#ifdef SQLITE_LEGACY_JSON_VALID + "LEGACY_JSON_VALID", +#endif #ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS "LIKE_DOESNT_MATCH_BLOBS", #endif @@ -22350,6 +22482,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ 0, /* bSmallMalloc */ 1, /* bExtraSchemaChecks */ + sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */ 0x7ffffffe, /* mxStrlen */ 0, /* neverCorrupt */ SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */ @@ -22579,6 +22712,9 @@ typedef struct VdbeSorter VdbeSorter; /* Elements of the linked list at Vdbe.pAuxData */ typedef struct AuxData AuxData; +/* A cache of large TEXT or BLOB values in a VdbeCursor */ +typedef struct VdbeTxtBlbCache VdbeTxtBlbCache; + /* Types of VDBE cursors */ #define CURTYPE_BTREE 0 #define CURTYPE_SORTER 1 @@ -22610,6 +22746,7 @@ struct VdbeCursor { Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ Bool noReuse:1; /* OpenEphemeral may not reuse this cursor */ + Bool colCache:1; /* pCache pointer is initialized and non-NULL */ u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */ union { /* pBtx for isEphermeral. pAltMap otherwise */ Btree *pBtx; /* Separate file holding temporary table */ @@ -22650,6 +22787,7 @@ struct VdbeCursor { #ifdef SQLITE_ENABLE_COLUMN_USED_MASK u64 maskUsed; /* Mask of columns used by this cursor */ #endif + VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */ /* 2*nField extra array elements allocated for aType[], beyond the one ** static element declared in the structure. nField total array slots for @@ -22662,12 +22800,25 @@ struct VdbeCursor { #define IsNullCursor(P) \ ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0) - /* ** A value for VdbeCursor.cacheStatus that means the cache is always invalid. */ #define CACHE_STALE 0 +/* +** Large TEXT or BLOB values can be slow to load, so we want to avoid +** loading them more than once. For that reason, large TEXT and BLOB values +** can be stored in a cache defined by this object, and attached to the +** VdbeCursor using the pCache field. +*/ +struct VdbeTxtBlbCache { + char *pCValue; /* A RCStr buffer to hold the value */ + i64 iOffset; /* File offset of the row being cached */ + int iCol; /* Column for which the cache is valid */ + u32 cacheStatus; /* Vdbe.cacheCtr value */ + u32 colCacheCtr; /* Column cache counter */ +}; + /* ** When a sub-program is executed (OP_Program), a structure of this type ** is allocated to store the current value of the program counter, as @@ -22988,16 +23139,18 @@ struct Vdbe { u32 nWrite; /* Number of write operations that have occurred */ #endif u16 nResColumn; /* Number of columns in one row of the result set */ + u16 nResAlloc; /* Column slots allocated to aColName[] */ u8 errorAction; /* Recovery action to do in case of an error */ u8 minWriteFileFormat; /* Minimum file format for writable database files */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 eVdbeState; /* On of the VDBE_*_STATE values */ bft expired:2; /* 1: recompile VM immediately 2: when convenient */ - bft explain:2; /* True if EXPLAIN present on SQL command */ + bft explain:2; /* 0: normal, 1: EXPLAIN, 2: EXPLAIN QUERY PLAN */ bft changeCntOn:1; /* True to update the change-counter */ bft usesStmtJournal:1; /* True if uses a statement journal */ bft readOnly:1; /* True for statements that do not write */ bft bIsReader:1; /* True for statements that read */ + bft haveEqpOps:1; /* Bytecode supports EXPLAIN QUERY PLAN */ yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ yDbMask lockMask; /* Subset of btreeMask that requires a lock */ u32 aCounter[9]; /* Counters used by sqlite3_stmt_status() */ @@ -23044,7 +23197,7 @@ struct PreUpdate { i64 iKey1; /* First key value passed to hook */ i64 iKey2; /* Second key value passed to hook */ Mem *aNew; /* Array of new.* values */ - Table *pTab; /* Schema object being upated */ + Table *pTab; /* Schema object being updated */ Index *pPk; /* PK index if pTab is WITHOUT ROWID */ }; @@ -23134,6 +23287,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetZeroBlob(Mem*,int); SQLITE_PRIVATE int sqlite3VdbeMemIsRowSet(const Mem*); #endif SQLITE_PRIVATE int sqlite3VdbeMemSetRowSet(Mem*); +SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem*, u8, u8); SQLITE_PRIVATE int sqlite3IntFloatCompare(i64,double); @@ -23730,8 +23884,8 @@ struct DateTime { */ static int getDigits(const char *zDate, const char *zFormat, ...){ /* The aMx[] array translates the 3rd character of each format - ** spec into a max size: a b c d e f */ - static const u16 aMx[] = { 12, 14, 24, 31, 59, 9999 }; + ** spec into a max size: a b c d e f */ + static const u16 aMx[] = { 12, 14, 24, 31, 59, 14712 }; va_list ap; int cnt = 0; char nextC; @@ -24072,17 +24226,14 @@ static void computeYMD(DateTime *p){ ** Compute the Hour, Minute, and Seconds from the julian day number. */ static void computeHMS(DateTime *p){ - int s; + int day_ms, day_min; /* milliseconds, minutes into the day */ if( p->validHMS ) return; computeJD(p); - s = (int)((p->iJD + 43200000) % 86400000); - p->s = s/1000.0; - s = (int)p->s; - p->s -= s; - p->h = s/3600; - s -= p->h*3600; - p->m = s/60; - p->s += s - p->m*60; + day_ms = (int)((p->iJD + 43200000) % 86400000); + p->s = (day_ms % 60000)/1000.0; + day_min = day_ms/60000; + p->m = day_min % 60; + p->h = day_min / 60; p->rawS = 0; p->validHMS = 1; } @@ -24261,6 +24412,25 @@ static const struct { { 4, "year", 14713.0, 31536000.0 }, }; +/* +** If the DateTime p is raw number, try to figure out if it is +** a julian day number of a unix timestamp. Set the p value +** appropriately. +*/ +static void autoAdjustDate(DateTime *p){ + if( !p->rawS || p->validJD ){ + p->rawS = 0; + }else if( p->s>=-21086676*(i64)10000 /* -4713-11-24 12:00:00 */ + && p->s<=(25340230*(i64)10000)+799 /* 9999-12-31 23:59:59 */ + ){ + double r = p->s*1000.0 + 210866760000000.0; + clearYMD_HMS_TZ(p); + p->iJD = (sqlite3_int64)(r + 0.5); + p->validJD = 1; + p->rawS = 0; + } +} + /* ** Process a modifier to a date-time stamp. The modifiers are ** as follows: @@ -24304,19 +24474,8 @@ static int parseModifier( */ if( sqlite3_stricmp(z, "auto")==0 ){ if( idx>1 ) return 1; /* IMP: R-33611-57934 */ - if( !p->rawS || p->validJD ){ - rc = 0; - p->rawS = 0; - }else if( p->s>=-21086676*(i64)10000 /* -4713-11-24 12:00:00 */ - && p->s<=(25340230*(i64)10000)+799 /* 9999-12-31 23:59:59 */ - ){ - r = p->s*1000.0 + 210866760000000.0; - clearYMD_HMS_TZ(p); - p->iJD = (sqlite3_int64)(r + 0.5); - p->validJD = 1; - p->rawS = 0; - rc = 0; - } + autoAdjustDate(p); + rc = 0; } break; } @@ -24482,18 +24641,73 @@ static int parseModifier( case '9': { double rRounder; int i; - for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){} + int Y,M,D,h,m,x; + const char *z2 = z; + char z0 = z[0]; + for(n=1; z[n]; n++){ + if( z[n]==':' ) break; + if( sqlite3Isspace(z[n]) ) break; + if( z[n]=='-' ){ + if( n==5 && getDigits(&z[1], "40f", &Y)==1 ) break; + if( n==6 && getDigits(&z[1], "50f", &Y)==1 ) break; + } + } if( sqlite3AtoF(z, &r, n, SQLITE_UTF8)<=0 ){ - rc = 1; + assert( rc==1 ); break; } - if( z[n]==':' ){ + if( z[n]=='-' ){ + /* A modifier of the form (+|-)YYYY-MM-DD adds or subtracts the + ** specified number of years, months, and days. MM is limited to + ** the range 0-11 and DD is limited to 0-30. + */ + if( z0!='+' && z0!='-' ) break; /* Must start with +/- */ + if( n==5 ){ + if( getDigits(&z[1], "40f-20a-20d", &Y, &M, &D)!=3 ) break; + }else{ + assert( n==6 ); + if( getDigits(&z[1], "50f-20a-20d", &Y, &M, &D)!=3 ) break; + z++; + } + if( M>=12 ) break; /* M range 0..11 */ + if( D>=31 ) break; /* D range 0..30 */ + computeYMD_HMS(p); + p->validJD = 0; + if( z0=='-' ){ + p->Y -= Y; + p->M -= M; + D = -D; + }else{ + p->Y += Y; + p->M += M; + } + x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; + p->Y += x; + p->M -= x*12; + computeJD(p); + p->validHMS = 0; + p->validYMD = 0; + p->iJD += (i64)D*86400000; + if( z[11]==0 ){ + rc = 0; + break; + } + if( sqlite3Isspace(z[11]) + && getDigits(&z[12], "20c:20e", &h, &m)==2 + ){ + z2 = &z[12]; + n = 2; + }else{ + break; + } + } + if( z2[n]==':' ){ /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the ** specified number of hours, minutes, seconds, and fractional seconds ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be ** omitted. */ - const char *z2 = z; + DateTime tx; sqlite3_int64 day; if( !sqlite3Isdigit(*z2) ) z2++; @@ -24503,7 +24717,7 @@ static int parseModifier( tx.iJD -= 43200000; day = tx.iJD/86400000; tx.iJD -= day*86400000; - if( z[0]=='-' ) tx.iJD = -tx.iJD; + if( z0=='-' ) tx.iJD = -tx.iJD; computeJD(p); clearYMD_HMS_TZ(p); p->iJD += tx.iJD; @@ -24519,7 +24733,7 @@ static int parseModifier( if( n>10 || n<3 ) break; if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--; computeJD(p); - rc = 1; + assert( rc==1 ); rRounder = r<0 ? -0.5 : +0.5; for(i=0; iM += (int)r; @@ -24687,7 +24900,7 @@ static void datetimeFunc( zBuf[16] = '0' + (x.m)%10; zBuf[17] = ':'; if( x.useSubsec ){ - s = (int)1000.0*x.s; + s = (int)(1000.0*x.s + 0.5); zBuf[18] = '0' + (s/10000)%10; zBuf[19] = '0' + (s/1000)%10; zBuf[20] = '.'; @@ -24734,7 +24947,7 @@ static void timeFunc( zBuf[4] = '0' + (x.m)%10; zBuf[5] = ':'; if( x.useSubsec ){ - s = (int)1000.0*x.s; + s = (int)(1000.0*x.s + 0.5); zBuf[6] = '0' + (s/10000)%10; zBuf[7] = '0' + (s/1000)%10; zBuf[8] = '.'; @@ -24805,7 +25018,7 @@ static void dateFunc( ** %M minute 00-59 ** %s seconds since 1970-01-01 ** %S seconds 00-59 -** %w day of week 0-6 sunday==0 +** %w day of week 0-6 Sunday==0 ** %W week of year 00-53 ** %Y year 0000-9999 ** %% % @@ -24945,6 +25158,117 @@ static void cdateFunc( dateFunc(context, 0, 0); } +/* +** timediff(DATE1, DATE2) +** +** Return the amount of time that must be added to DATE2 in order to +** convert it into DATE2. The time difference format is: +** +** +YYYY-MM-DD HH:MM:SS.SSS +** +** The initial "+" becomes "-" if DATE1 occurs before DATE2. For +** date/time values A and B, the following invariant should hold: +** +** datetime(A) == (datetime(B, timediff(A,B)) +** +** Both DATE arguments must be either a julian day number, or an +** ISO-8601 string. The unix timestamps are not supported by this +** routine. +*/ +static void timediffFunc( + sqlite3_context *context, + int NotUsed1, + sqlite3_value **argv +){ + char sign; + int Y, M; + DateTime d1, d2; + sqlite3_str sRes; + UNUSED_PARAMETER(NotUsed1); + if( isDate(context, 1, &argv[0], &d1) ) return; + if( isDate(context, 1, &argv[1], &d2) ) return; + computeYMD_HMS(&d1); + computeYMD_HMS(&d2); + if( d1.iJD>=d2.iJD ){ + sign = '+'; + Y = d1.Y - d2.Y; + if( Y ){ + d2.Y = d1.Y; + d2.validJD = 0; + computeJD(&d2); + } + M = d1.M - d2.M; + if( M<0 ){ + Y--; + M += 12; + } + if( M!=0 ){ + d2.M = d1.M; + d2.validJD = 0; + computeJD(&d2); + } + while( d1.iJDd2.iJD ){ + M--; + if( M<0 ){ + M = 11; + Y--; + } + d2.M++; + if( d2.M>12 ){ + d2.M = 1; + d2.Y++; + } + d2.validJD = 0; + computeJD(&d2); + } + d1.iJD = d2.iJD - d1.iJD; + d1.iJD += (u64)1486995408 * (u64)100000; + } + d1.validYMD = 0; + d1.validHMS = 0; + d1.validTZ = 0; + computeYMD_HMS(&d1); + sqlite3StrAccumInit(&sRes, 0, 0, 0, 100); + sqlite3_str_appendf(&sRes, "%c%04d-%02d-%02d %02d:%02d:%06.3f", + sign, Y, M, d1.D-1, d1.h, d1.m, d1.s); + sqlite3ResultStrAccum(context, &sRes); +} + + /* ** current_timestamp() ** @@ -25019,6 +25343,7 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){ PURE_DATE(time, -1, 0, 0, timeFunc ), PURE_DATE(datetime, -1, 0, 0, datetimeFunc ), PURE_DATE(strftime, -1, 0, 0, strftimeFunc ), + PURE_DATE(timediff, 2, 0, 0, timediffFunc ), DFUNCTION(current_time, 0, 0, 0, ctimeFunc ), DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), DFUNCTION(current_date, 0, 0, 0, cdateFunc ), @@ -25172,7 +25497,7 @@ SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite ** is using a regular VFS, it is called after the corresponding ** transaction has been committed. Injecting a fault at this point - ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM + ** confuses the test scripts - the COMMIT command returns SQLITE_NOMEM ** but the transaction is committed anyway. ** ** The core must call OsFileControl() though, not OsFileControlHint(), @@ -25793,7 +26118,7 @@ static void *sqlite3MemMalloc(int nByte){ ** or sqlite3MemRealloc(). ** ** For this low-level routine, we already know that pPrior!=0 since -** cases where pPrior==0 will have been intecepted and dealt with +** cases where pPrior==0 will have been intercepted and dealt with ** by higher-level routines. */ static void sqlite3MemFree(void *pPrior){ @@ -25881,7 +26206,7 @@ static int sqlite3MemInit(void *NotUsed){ return SQLITE_OK; } len = sizeof(cpuCount); - /* One usually wants to use hw.acctivecpu for MT decisions, but not here */ + /* One usually wants to use hw.activecpu for MT decisions, but not here */ sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0); if( cpuCount>1 ){ /* defer MT decisions to system malloc */ @@ -28348,7 +28673,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ /* ** The sqlite3_mutex.id, sqlite3_mutex.nRef, and sqlite3_mutex.owner fields -** are necessary under two condidtions: (1) Debug builds and (2) using +** are necessary under two conditions: (1) Debug builds and (2) using ** home-grown mutexes. Encapsulate these conditions into a single #define. */ #if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX) @@ -28849,7 +29174,7 @@ struct sqlite3_mutex { CRITICAL_SECTION mutex; /* Mutex controlling the lock */ int id; /* Mutex type */ #ifdef SQLITE_DEBUG - volatile int nRef; /* Number of enterances */ + volatile int nRef; /* Number of entrances */ volatile DWORD owner; /* Thread holding this mutex */ volatile LONG trace; /* True to trace changes */ #endif @@ -30221,57 +30546,6 @@ static const et_info fmtinfo[] = { ** %!S Like %S but prefer the zName over the zAlias */ -/* Floating point constants used for rounding */ -static const double arRound[] = { - 5.0e-01, 5.0e-02, 5.0e-03, 5.0e-04, 5.0e-05, - 5.0e-06, 5.0e-07, 5.0e-08, 5.0e-09, 5.0e-10, -}; - -/* -** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point -** conversions will work. -*/ -#ifndef SQLITE_OMIT_FLOATING_POINT -/* -** "*val" is a double such that 0.1 <= *val < 10.0 -** Return the ascii code for the leading digit of *val, then -** multiply "*val" by 10.0 to renormalize. -** -** Example: -** input: *val = 3.14159 -** output: *val = 1.4159 function return = '3' -** -** The counter *cnt is incremented each time. After counter exceeds -** 16 (the number of significant digits in a 64-bit float) '0' is -** always returned. -*/ -static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ - int digit; - LONGDOUBLE_TYPE d; - if( (*cnt)<=0 ) return '0'; - (*cnt)--; - digit = (int)*val; - d = digit; - digit += '0'; - *val = (*val - d)*10.0; - return (char)digit; -} -#endif /* SQLITE_OMIT_FLOATING_POINT */ - -#ifndef SQLITE_OMIT_FLOATING_POINT -/* -** "*val" is a u64. *msd is a divisor used to extract the -** most significant digit of *val. Extract that most significant -** digit and return it. -*/ -static char et_getdigit_int(u64 *val, u64 *msd){ - u64 x = (*val)/(*msd); - *val -= x*(*msd); - if( *msd>=10 ) *msd /= 10; - return '0' + (char)(x & 15); -} -#endif /* SQLITE_OMIT_FLOATING_POINT */ - /* ** Set the StrAccum object to an error mode. */ @@ -30363,20 +30637,15 @@ SQLITE_API void sqlite3_str_vappendf( u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ sqlite_uint64 longvalue; /* Value for integer types */ - LONGDOUBLE_TYPE realvalue; /* Value for real types */ - sqlite_uint64 msd; /* Divisor to get most-significant-digit - ** of longvalue */ + double realvalue; /* Value for real types */ const et_info *infop; /* Pointer to the appropriate info structure */ char *zOut; /* Rendering buffer */ int nOut; /* Size of the rendering buffer */ char *zExtra = 0; /* Malloced memory used by some conversion */ -#ifndef SQLITE_OMIT_FLOATING_POINT - int exp, e2; /* exponent of real numbers */ - int nsd; /* Number of significant digits returned */ - double rounder; /* Used for rounding floating point values */ + int exp, e2; /* exponent of real numbers */ etByte flag_dp; /* True if decimal point should be shown */ etByte flag_rtz; /* True if trailing zeros should be removed */ -#endif + PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ char buf[etBUFSIZE]; /* Conversion buffer */ @@ -30651,94 +30920,61 @@ SQLITE_API void sqlite3_str_vappendf( break; case etFLOAT: case etEXP: - case etGENERIC: + case etGENERIC: { + FpDecode s; + int iRound; + int j; + if( bArgList ){ realvalue = getDoubleArg(pArgList); }else{ realvalue = va_arg(ap,double); } -#ifdef SQLITE_OMIT_FLOATING_POINT - length = 0; -#else if( precision<0 ) precision = 6; /* Set default precision */ #ifdef SQLITE_FP_PRECISION_LIMIT if( precision>SQLITE_FP_PRECISION_LIMIT ){ precision = SQLITE_FP_PRECISION_LIMIT; } #endif - if( realvalue<0.0 ){ - realvalue = -realvalue; + if( xtype==etFLOAT ){ + iRound = -precision; + }else if( xtype==etGENERIC ){ + iRound = precision; + }else{ + iRound = precision+1; + } + sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 26 : 16); + if( s.isSpecial ){ + if( s.isSpecial==2 ){ + bufpt = flag_zeropad ? "null" : "NaN"; + length = sqlite3Strlen30(bufpt); + break; + }else if( flag_zeropad ){ + s.z[0] = '9'; + s.iDP = 1000; + s.n = 1; + }else{ + memcpy(buf, "-Inf", 5); + bufpt = buf; + if( s.sign=='-' ){ + /* no-op */ + }else if( flag_prefix ){ + buf[0] = flag_prefix; + }else{ + bufpt++; + } + length = sqlite3Strlen30(bufpt); + break; + } + } + if( s.sign=='-' ){ prefix = '-'; }else{ prefix = flag_prefix; } - exp = 0; - if( xtype==etGENERIC && precision>0 ) precision--; - testcase( precision>0xfff ); - if( realvalue<1.0e+16 - && realvalue==(LONGDOUBLE_TYPE)(longvalue = (u64)realvalue) - ){ - /* Number is a pure integer that can be represented as u64 */ - for(msd=1; msd*10<=longvalue; msd *= 10, exp++){} - if( exp>precision && xtype!=etFLOAT ){ - u64 rnd = msd/2; - int kk = precision; - while( kk-- > 0 ){ rnd /= 10; } - longvalue += rnd; - } - }else{ - msd = 0; - longvalue = 0; /* To prevent a compiler warning */ - idx = precision & 0xfff; - rounder = arRound[idx%10]; - while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; } - if( xtype==etFLOAT ){ - double rx = (double)realvalue; - sqlite3_uint64 u; - int ex; - memcpy(&u, &rx, sizeof(u)); - ex = -1023 + (int)((u>>52)&0x7ff); - if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16; - realvalue += rounder; - } - if( sqlite3IsNaN((double)realvalue) ){ - if( flag_zeropad ){ - bufpt = "null"; - length = 4; - }else{ - bufpt = "NaN"; - length = 3; - } - break; - } - /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ - if( ALWAYS(realvalue>0.0) ){ - LONGDOUBLE_TYPE scale = 1.0; - while( realvalue>=1e100*scale && exp<=350){ scale*=1e100;exp+=100;} - while( realvalue>=1e10*scale && exp<=350 ){ scale*=1e10; exp+=10; } - while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; } - realvalue /= scale; - while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; } - while( realvalue<1.0 ){ realvalue *= 10.0; exp--; } - if( exp>350 ){ - if( flag_zeropad ){ - realvalue = 9.0; - exp = 999; - }else{ - bufpt = buf; - buf[0] = prefix; - memcpy(buf+(prefix!=0),"Inf",4); - length = 3+(prefix!=0); - break; - } - } - if( xtype!=etFLOAT ){ - realvalue += rounder; - if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } - } - } - } + exp = s.iDP-1; + if( xtype==etGENERIC && precision>0 ) precision--; /* ** If the field type is etGENERIC, then convert to either etEXP @@ -30758,9 +30994,8 @@ SQLITE_API void sqlite3_str_vappendf( if( xtype==etEXP ){ e2 = 0; }else{ - e2 = exp; + e2 = s.iDP - 1; } - nsd = 16 + flag_altform2*10; bufpt = buf; { i64 szBufNeeded; /* Size of a temporary buffer needed */ @@ -30778,16 +31013,12 @@ SQLITE_API void sqlite3_str_vappendf( *(bufpt++) = prefix; } /* Digits prior to the decimal point */ + j = 0; if( e2<0 ){ *(bufpt++) = '0'; - }else if( msd>0 ){ - for(; e2>=0; e2--){ - *(bufpt++) = et_getdigit_int(&longvalue,&msd); - if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ','; - } }else{ for(; e2>=0; e2--){ - *(bufpt++) = et_getdigit(&realvalue,&nsd); + *(bufpt++) = j1 ) *(bufpt++) = ','; } } @@ -30797,19 +31028,12 @@ SQLITE_API void sqlite3_str_vappendf( } /* "0" digits after the decimal point but before the first ** significant digit of the number */ - for(e2++; e2<0; precision--, e2++){ - assert( precision>0 ); + for(e2++; e2<0 && precision>0; precision--, e2++){ *(bufpt++) = '0'; } /* Significant digits after the decimal point */ - if( msd>0 ){ - while( (precision--)>0 ){ - *(bufpt++) = et_getdigit_int(&longvalue,&msd); - } - }else{ - while( (precision--)>0 ){ - *(bufpt++) = et_getdigit(&realvalue,&nsd); - } + while( (precision--)>0 ){ + *(bufpt++) = jcharset]; if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; @@ -30858,8 +31083,8 @@ SQLITE_API void sqlite3_str_vappendf( while( nPad-- ) bufpt[i++] = '0'; length = width; } -#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */ break; + } case etSIZE: if( !bArgList ){ *(va_arg(ap,int*)) = pAccum->nChar; @@ -31583,6 +31808,75 @@ SQLITE_API void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){ va_end(ap); } + +/***************************************************************************** +** Reference counted string storage +*****************************************************************************/ + +/* +** Increase the reference count of the string by one. +** +** The input parameter is returned. +*/ +SQLITE_PRIVATE char *sqlite3RCStrRef(char *z){ + RCStr *p = (RCStr*)z; + assert( p!=0 ); + p--; + p->nRCRef++; + return z; +} + +/* +** Decrease the reference count by one. Free the string when the +** reference count reaches zero. +*/ +SQLITE_PRIVATE void sqlite3RCStrUnref(char *z){ + RCStr *p = (RCStr*)z; + assert( p!=0 ); + p--; + assert( p->nRCRef>0 ); + if( p->nRCRef>=2 ){ + p->nRCRef--; + }else{ + sqlite3_free(p); + } +} + +/* +** Create a new string that is capable of holding N bytes of text, not counting +** the zero byte at the end. The string is uninitialized. +** +** The reference count is initially 1. Call sqlite3RCStrUnref() to free the +** newly allocated string. +** +** This routine returns 0 on an OOM. +*/ +SQLITE_PRIVATE char *sqlite3RCStrNew(u64 N){ + RCStr *p = sqlite3_malloc64( N + sizeof(*p) + 1 ); + if( p==0 ) return 0; + p->nRCRef = 1; + return (char*)&p[1]; +} + +/* +** Change the size of the string so that it is able to hold N bytes. +** The string might be reallocated, so return the new allocation. +*/ +SQLITE_PRIVATE char *sqlite3RCStrResize(char *z, u64 N){ + RCStr *p = (RCStr*)z; + RCStr *pNew; + assert( p!=0 ); + p--; + assert( p->nRCRef==1 ); + pNew = sqlite3_realloc64(p, N+sizeof(RCStr)+1); + if( pNew==0 ){ + sqlite3_free(p); + return 0; + }else{ + return (char*)&pNew[1]; + } +} + /************** End of printf.c **********************************************/ /************** Begin file treeview.c ****************************************/ /* @@ -32230,7 +32524,8 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m }; assert( pExpr->op2==TK_IS || pExpr->op2==TK_ISNOT ); assert( pExpr->pRight ); - assert( sqlite3ExprSkipCollate(pExpr->pRight)->op==TK_TRUEFALSE ); + assert( sqlite3ExprSkipCollateAndLikely(pExpr->pRight)->op + == TK_TRUEFALSE ); x = (pExpr->op2==TK_ISNOT)*2 + sqlite3ExprTruthValue(pExpr->pRight); zUniOp = azOp[x]; break; @@ -33889,7 +34184,7 @@ SQLITE_PRIVATE void sqlite3UtfSelfTest(void){ /* ** Calls to sqlite3FaultSim() are used to simulate a failure during testing, ** or to bypass normal error detection during testing in order to let -** execute proceed futher downstream. +** execute proceed further downstream. ** ** In deployment, sqlite3FaultSim() *always* return SQLITE_OK (0). The ** sqlite3FaultSim() function only returns non-zero during testing. @@ -34006,6 +34301,23 @@ SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3 *db){ */ SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){ if( rc==SQLITE_IOERR_NOMEM ) return; +#ifdef SQLITE_USE_SEH + if( rc==SQLITE_IOERR_IN_PAGE ){ + int ii; + int iErr; + sqlite3BtreeEnterAll(db); + for(ii=0; iinDb; ii++){ + if( db->aDb[ii].pBt ){ + iErr = sqlite3PagerWalSystemErrno(sqlite3BtreePager(db->aDb[ii].pBt)); + if( iErr ){ + db->iSysErrno = iErr; + } + } + } + sqlite3BtreeLeaveAll(db); + return; + } +#endif rc &= 0xff; if( rc==SQLITE_CANTOPEN || rc==SQLITE_IOERR ){ db->iSysErrno = sqlite3OsGetLastError(db->pVfs); @@ -34251,43 +34563,40 @@ SQLITE_PRIVATE u8 sqlite3StrIHash(const char *z){ return h; } -/* -** Compute 10 to the E-th power. Examples: E==1 results in 10. -** E==2 results in 100. E==50 results in 1.0e50. +/* Double-Double multiplication. (x[0],x[1]) *= (y,yy) ** -** This routine only works for values of E between 1 and 341. +** Reference: +** T. J. Dekker, "A Floating-Point Technique for Extending the +** Available Precision". 1971-07-26. */ -static LONGDOUBLE_TYPE sqlite3Pow10(int E){ -#if defined(_MSC_VER) - static const LONGDOUBLE_TYPE x[] = { - 1.0e+001L, - 1.0e+002L, - 1.0e+004L, - 1.0e+008L, - 1.0e+016L, - 1.0e+032L, - 1.0e+064L, - 1.0e+128L, - 1.0e+256L - }; - LONGDOUBLE_TYPE r = 1.0; - int i; - assert( E>=0 && E<=307 ); - for(i=0; E!=0; i++, E >>=1){ - if( E & 1 ) r *= x[i]; - } - return r; -#else - LONGDOUBLE_TYPE x = 10.0; - LONGDOUBLE_TYPE r = 1.0; - while(1){ - if( E & 1 ) r *= x; - E >>= 1; - if( E==0 ) break; - x *= x; - } - return r; -#endif +static void dekkerMul2(volatile double *x, double y, double yy){ + /* + ** The "volatile" keywords on parameter x[] and on local variables + ** below are needed force intermediate results to be truncated to + ** binary64 rather than be carried around in an extended-precision + ** format. The truncation is necessary for the Dekker algorithm to + ** work. Intel x86 floating point might omit the truncation without + ** the use of volatile. + */ + volatile double tx, ty, p, q, c, cc; + double hx, hy; + u64 m; + memcpy(&m, (void*)&x[0], 8); + m &= 0xfffffffffc000000LL; + memcpy(&hx, &m, 8); + tx = x[0] - hx; + memcpy(&m, &y, 8); + m &= 0xfffffffffc000000LL; + memcpy(&hy, &m, 8); + ty = y - hy; + p = hx*hy; + q = hx*ty + tx*hy; + c = p+q; + cc = p - c + q + tx*ty; + cc = x[0]*yy + x[1]*y + cc; + x[0] = c + cc; + x[1] = c - x[0]; + x[1] += cc; } /* @@ -34328,12 +34637,11 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en const char *zEnd; /* sign * significand * (10 ^ (esign * exponent)) */ int sign = 1; /* sign of significand */ - i64 s = 0; /* significand */ + u64 s = 0; /* significand */ int d = 0; /* adjust exponent for shifting decimal point */ int esign = 1; /* sign of exponent */ int e = 0; /* exponent */ int eValid = 1; /* True exponent is either not used or is well-formed */ - double result; int nDigit = 0; /* Number of digits processed */ int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */ @@ -34373,7 +34681,7 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en while( z=((LARGEST_INT64-9)/10) ){ + if( s>=((LARGEST_UINT64-9)/10) ){ /* skip non-significant significand digits ** (increase exponent by d to shift decimal left) */ while( z0 && s<(LARGEST_UINT64/10) ){ + s *= 10; + e--; + } + while( e<0 && (s%10)==0 ){ + s /= 10; + e++; } - if( s==0 ) { - /* In the IEEE 754 standard, zero is signed. */ - result = sign<0 ? -(double)0 : (double)0; - } else { - /* Attempt to reduce exponent. - ** - ** Branches that are not required for the correct answer but which only - ** help to obtain the correct answer faster are marked with special - ** comments, as a hint to the mutation tester. - */ - while( e>0 ){ /*OPTIMIZATION-IF-TRUE*/ - if( esign>0 ){ - if( s>=(LARGEST_INT64/10) ) break; /*OPTIMIZATION-IF-FALSE*/ - s *= 10; - }else{ - if( s%10!=0 ) break; /*OPTIMIZATION-IF-FALSE*/ - s /= 10; - } - e--; - } - - /* adjust the sign of significand */ - s = sign<0 ? -s : s; - - if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/ - result = (double)s; + if( e==0 ){ + *pResult = s; + }else if( sqlite3Config.bUseLongDouble ){ + LONGDOUBLE_TYPE r = (LONGDOUBLE_TYPE)s; + if( e>0 ){ + while( e>=100 ){ e-=100; r *= 1.0e+100L; } + while( e>=10 ){ e-=10; r *= 1.0e+10L; } + while( e>=1 ){ e-=1; r *= 1.0e+01L; } }else{ - /* attempt to handle extremely small/large numbers better */ - if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/ - if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/ - LONGDOUBLE_TYPE scale = sqlite3Pow10(e-308); - if( esign<0 ){ - result = s / scale; - result /= 1.0e+308; - }else{ - result = s * scale; - result *= 1.0e+308; - } - }else{ assert( e>=342 ); - if( esign<0 ){ - result = 0.0*s; - }else{ + while( e<=-100 ){ e+=100; r *= 1.0e-100L; } + while( e<=-10 ){ e+=10; r *= 1.0e-10L; } + while( e<=-1 ){ e+=1; r *= 1.0e-01L; } + } + assert( r>=0.0 ); + if( r>+1.7976931348623157081452742373e+308L ){ #ifdef INFINITY - result = INFINITY*s; + *pResult = +INFINITY; #else - result = 1e308*1e308*s; /* Infinity */ + *pResult = 1.0e308*10.0; #endif - } - } - }else{ - LONGDOUBLE_TYPE scale = sqlite3Pow10(e); - if( esign<0 ){ - result = s / scale; - }else{ - result = s * scale; - } + }else{ + *pResult = (double)r; + } + }else{ + double rr[2]; + u64 s2; + rr[0] = (double)s; + s2 = (u64)rr[0]; + rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); + if( e>0 ){ + while( e>=100 ){ + e -= 100; + dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); + } + while( e>=10 ){ + e -= 10; + dekkerMul2(rr, 1.0e+10, 0.0); + } + while( e>=1 ){ + e -= 1; + dekkerMul2(rr, 1.0e+01, 0.0); + } + }else{ + while( e<=-100 ){ + e += 100; + dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); + } + while( e<=-10 ){ + e += 10; + dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); + } + while( e<=-1 ){ + e += 1; + dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); } } + *pResult = rr[0]+rr[1]; + if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300; } + if( sign<0 ) *pResult = -*pResult; + assert( !sqlite3IsNaN(*pResult) ); - /* store the result */ - *pResult = result; - - /* return true if number and no extra non-whitespace chracters after */ +atof_return: + /* return true if number and no extra non-whitespace characters after */ if( z==zEnd && nDigit>0 && eValid && eType>0 ){ return eType; }else if( eType>=2 && (eType==3 || eValid) && nDigit>0 ){ @@ -34636,7 +34954,7 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc /* This test and assignment is needed only to suppress UB warnings ** from clang and -fsanitize=undefined. This test and assignment make ** the code a little larger and slower, and no harm comes from omitting - ** them, but we must appaise the undefined-behavior pharisees. */ + ** them, but we must appease the undefined-behavior pharisees. */ *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; }else if( neg ){ *pNum = -(i64)u; @@ -34714,7 +35032,9 @@ SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char *z, i64 *pOut){ }else #endif /* SQLITE_OMIT_HEX_INTEGER */ { - return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8); + int n = (int)(0x3fffffff&strspn(z,"+- \n\t0123456789")); + if( z[n] ) n++; + return sqlite3Atoi64(z, pOut, n, SQLITE_UTF8); } } @@ -34793,6 +35113,153 @@ SQLITE_PRIVATE int sqlite3Atoi(const char *z){ return x; } +/* +** Decode a floating-point value into an approximate decimal +** representation. +** +** Round the decimal representation to n significant digits if +** n is positive. Or round to -n signficant digits after the +** decimal point if n is negative. No rounding is performed if +** n is zero. +** +** The significant digits of the decimal representation are +** stored in p->z[] which is a often (but not always) a pointer +** into the middle of p->zBuf[]. There are p->n significant digits. +** The p->z[] array is *not* zero-terminated. +*/ +SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ + int i; + u64 v; + int e, exp = 0; + p->isSpecial = 0; + p->z = p->zBuf; + + /* Convert negative numbers to positive. Deal with Infinity, 0.0, and + ** NaN. */ + if( r<0.0 ){ + p->sign = '-'; + r = -r; + }else if( r==0.0 ){ + p->sign = '+'; + p->n = 1; + p->iDP = 1; + p->z = "0"; + return; + }else{ + p->sign = '+'; + } + memcpy(&v,&r,8); + e = v>>52; + if( (e&0x7ff)==0x7ff ){ + p->isSpecial = 1 + (v!=0x7ff0000000000000LL); + p->n = 0; + p->iDP = 0; + return; + } + + /* Multiply r by powers of ten until it lands somewhere in between + ** 1.0e+19 and 1.0e+17. + */ + if( sqlite3Config.bUseLongDouble ){ + LONGDOUBLE_TYPE rr = r; + if( rr>=1.0e+19 ){ + while( rr>=1.0e+119L ){ exp+=100; rr *= 1.0e-100L; } + while( rr>=1.0e+29L ){ exp+=10; rr *= 1.0e-10L; } + while( rr>=1.0e+19L ){ exp++; rr *= 1.0e-1L; } + }else{ + while( rr<1.0e-97L ){ exp-=100; rr *= 1.0e+100L; } + while( rr<1.0e+07L ){ exp-=10; rr *= 1.0e+10L; } + while( rr<1.0e+17L ){ exp--; rr *= 1.0e+1L; } + } + v = (u64)rr; + }else{ + /* If high-precision floating point is not available using "long double", + ** then use Dekker-style double-double computation to increase the + ** precision. + ** + ** The error terms on constants like 1.0e+100 computed using the + ** decimal extension, for example as follows: + ** + ** SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100))); + */ + double rr[2]; + rr[0] = r; + rr[1] = 0.0; + if( rr[0]>1.84e+19 ){ + while( rr[0]>1.84e+119 ){ + exp += 100; + dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); + } + while( rr[0]>1.84e+29 ){ + exp += 10; + dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); + } + while( rr[0]>1.84e+19 ){ + exp += 1; + dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); + } + }else{ + while( rr[0]<1.84e-82 ){ + exp -= 100; + dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); + } + while( rr[0]<1.84e+08 ){ + exp -= 10; + dekkerMul2(rr, 1.0e+10, 0.0); + } + while( rr[0]<1.84e+18 ){ + exp -= 1; + dekkerMul2(rr, 1.0e+01, 0.0); + } + } + v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1]; + } + + + /* Extract significant digits. */ + i = sizeof(p->zBuf)-1; + assert( v>0 ); + while( v ){ p->zBuf[i--] = (v%10) + '0'; v /= 10; } + assert( i>=0 && izBuf)-1 ); + p->n = sizeof(p->zBuf) - 1 - i; + assert( p->n>0 ); + assert( p->nzBuf) ); + p->iDP = p->n + exp; + if( iRound<0 ){ + iRound = p->iDP - iRound; + if( iRound==0 && p->zBuf[i+1]>='5' ){ + iRound = 1; + p->zBuf[i--] = '0'; + p->n++; + p->iDP++; + } + } + if( iRound>0 && (iRoundn || p->n>mxRound) ){ + char *z = &p->zBuf[i+1]; + if( iRound>mxRound ) iRound = mxRound; + p->n = iRound; + if( z[iRound]>='5' ){ + int j = iRound-1; + while( 1 /*exit-by-break*/ ){ + z[j]++; + if( z[j]<='9' ) break; + z[j] = '0'; + if( j==0 ){ + p->z[i--] = '1'; + p->n++; + p->iDP++; + break; + }else{ + j--; + } + } + } + } + p->z = &p->zBuf[i+1]; + assert( i+p->n < sizeof(p->zBuf) ); + while( ALWAYS(p->n>0) && p->z[p->n-1]=='0' ){ p->n--; } +} + /* ** Try to convert z into an unsigned 32-bit integer. Return true on ** success and false if there is an error. @@ -35321,7 +35788,7 @@ SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3 *db){ } /* -** Attempt to add, substract, or multiply the 64-bit signed value iB against +** Attempt to add, subtract, or multiply the 64-bit signed value iB against ** the other 64-bit signed integer at *pA and store the result in *pA. ** Return 0 on success. Or if the operation would have resulted in an ** overflow, leave *pA unchanged and return 1. @@ -35634,7 +36101,7 @@ SQLITE_PRIVATE int sqlite3VListNameToNum(VList *pIn, const char *zName, int nNam #define SQLITE_HWTIME_H /* -** The following routine only works on pentium-class (or newer) processors. +** The following routine only works on Pentium-class (or newer) processors. ** It uses the RDTSC opcode to read the cycle count value out of the ** processor and returns that value. This can be used for high-res ** profiling. @@ -35806,7 +36273,7 @@ static void insertElement( } -/* Resize the hash table so that it cantains "new_size" buckets. +/* Resize the hash table so that it contains "new_size" buckets. ** ** The hash table might fail to resize if sqlite3_malloc() fails or ** if the new size is the same as the prior size. @@ -37192,7 +37659,7 @@ SQLITE_PRIVATE int sqlite3KvvfsInit(void){ ** This source file is organized into divisions where the logic for various ** subfunctions is contained within the appropriate division. PLEASE ** KEEP THE STRUCTURE OF THIS FILE INTACT. New code should be placed -** in the correct division and should be clearly labeled. +** in the correct division and should be clearly labelled. ** ** The layout of divisions is as follows: ** @@ -37779,7 +38246,7 @@ static int robustFchown(int fd, uid_t uid, gid_t gid){ /* ** This is the xSetSystemCall() method of sqlite3_vfs for all of the -** "unix" VFSes. Return SQLITE_OK opon successfully updating the +** "unix" VFSes. Return SQLITE_OK upon successfully updating the ** system call pointer, or SQLITE_NOTFOUND if there is no configurable ** system call named zName. */ @@ -38301,7 +38768,7 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){ ** If you close a file descriptor that points to a file that has locks, ** all locks on that file that are owned by the current process are ** released. To work around this problem, each unixInodeInfo object -** maintains a count of the number of pending locks on tha inode. +** maintains a count of the number of pending locks on the inode. ** When an attempt is made to close an unixFile, if there are ** other unixFile open on the same inode that are holding locks, the call ** to close() the file descriptor is deferred until all of the locks clear. @@ -38315,7 +38782,7 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){ ** not posix compliant. Under LinuxThreads, a lock created by thread ** A cannot be modified or overridden by a different thread B. ** Only thread A can modify the lock. Locking behavior is correct -** if the appliation uses the newer Native Posix Thread Library (NPTL) +** if the application uses the newer Native Posix Thread Library (NPTL) ** on linux - with NPTL a lock created by thread A can override locks ** in thread B. But there is no way to know at compile-time which ** threading library is being used. So there is no way to know at @@ -38517,7 +38984,7 @@ static void storeLastErrno(unixFile *pFile, int error){ } /* -** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. +** Close all file descriptors accumulated in the unixInodeInfo->pUnused list. */ static void closePendingFds(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; @@ -38880,7 +39347,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ ** slightly in order to be compatible with Windows95 systems simultaneously ** accessing the same database file, in case that is ever required. ** - ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved + ** Symbols defined in os.h identify the 'pending byte' and the 'reserved ** byte', each single bytes at well known offsets, and the 'shared byte ** range', a range of 510 bytes at a well known offset. ** @@ -38888,7 +39355,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ ** byte'. If this is successful, 'shared byte range' is read-locked ** and the lock on the 'pending byte' released. (Legacy note: When ** SQLite was first developed, Windows95 systems were still very common, - ** and Widnows95 lacks a shared-lock capability. So on Windows95, a + ** and Windows95 lacks a shared-lock capability. So on Windows95, a ** single randomly selected by from the 'shared byte range' is locked. ** Windows95 is now pretty much extinct, but this work-around for the ** lack of shared-locks on Windows95 lives on, for backwards @@ -38909,7 +39376,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ ** obtaining a write-lock on the 'pending byte'. This ensures that no new ** SHARED locks can be obtained, but existing SHARED locks are allowed to ** persist. If the call to this function fails to obtain the EXCLUSIVE - ** lock in this case, it holds the PENDING lock intead. The client may + ** lock in this case, it holds the PENDING lock instead. The client may ** then re-attempt the EXCLUSIVE lock later on, after existing SHARED ** locks have cleared. */ @@ -38937,7 +39404,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ /* Make sure the locking sequence is correct. ** (1) We never move from unlocked to anything higher than shared lock. - ** (2) SQLite never explicitly requests a pendig lock. + ** (2) SQLite never explicitly requests a pending lock. ** (3) A shared lock is always held when a reserve lock is requested. */ assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); @@ -40155,7 +40622,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){ /* Make sure the locking sequence is correct ** (1) We never move from unlocked to anything higher than shared lock. - ** (2) SQLite never explicitly requests a pendig lock. + ** (2) SQLite never explicitly requests a pending lock. ** (3) A shared lock is always held when a reserve lock is requested. */ assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); @@ -40271,7 +40738,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){ if( !(failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST + pInode->sharedByte, 1, 0)) ){ int failed2 = SQLITE_OK; - /* now attemmpt to get the exclusive lock range */ + /* now attempt to get the exclusive lock range */ failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 1); if( failed && (failed2 = afpSetLock(context->dbPath, pFile, @@ -40566,7 +41033,7 @@ static int unixRead( #endif #if SQLITE_MAX_MMAP_SIZE>0 - /* Deal with as much of this read request as possible by transfering + /* Deal with as much of this read request as possible by transferring ** data from the memory mapping using memcpy(). */ if( offsetmmapSize ){ if( offset+amt <= pFile->mmapSize ){ @@ -40718,7 +41185,7 @@ static int unixWrite( #endif #if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 - /* Deal with as much of this write request as possible by transfering + /* Deal with as much of this write request as possible by transferring ** data from the memory mapping using memcpy(). */ if( offsetmmapSize ){ if( offset+amt <= pFile->mmapSize ){ @@ -40840,7 +41307,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){ /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a ** no-op. But go ahead and call fstat() to validate the file ** descriptor as we need a method to provoke a failure during - ** coverate testing. + ** coverage testing. */ #ifdef SQLITE_NO_SYNC { @@ -43885,12 +44352,17 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ ** than the argument. */ static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){ -#if OS_VXWORKS || _POSIX_C_SOURCE >= 199309L +#if !defined(HAVE_NANOSLEEP) || HAVE_NANOSLEEP+0 struct timespec sp; - sp.tv_sec = microseconds / 1000000; sp.tv_nsec = (microseconds % 1000000) * 1000; + + /* Almost all modern unix systems support nanosleep(). But if you are + ** compiling for one of the rare exceptions, you can use + ** -DHAVE_NANOSLEEP=0 (perhaps in conjuction with -DHAVE_USLEEP if + ** usleep() is available) in order to bypass the use of nanosleep() */ nanosleep(&sp, NULL); + UNUSED_PARAMETER(NotUsed); return microseconds; #elif defined(HAVE_USLEEP) && HAVE_USLEEP @@ -46480,7 +46952,7 @@ static struct win_syscall { /* ** This is the xSetSystemCall() method of sqlite3_vfs for all of the -** "win32" VFSes. Return SQLITE_OK opon successfully updating the +** "win32" VFSes. Return SQLITE_OK upon successfully updating the ** system call pointer, or SQLITE_NOTFOUND if there is no configurable ** system call named zName. */ @@ -48060,7 +48532,7 @@ static int winRead( pFile->h, pBuf, amt, offset, pFile->locktype)); #if SQLITE_MAX_MMAP_SIZE>0 - /* Deal with as much of this read request as possible by transfering + /* Deal with as much of this read request as possible by transferring ** data from the memory mapping using memcpy(). */ if( offsetmmapSize ){ if( offset+amt <= pFile->mmapSize ){ @@ -48138,7 +48610,7 @@ static int winWrite( pFile->h, pBuf, amt, offset, pFile->locktype)); #if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 - /* Deal with as much of this write request as possible by transfering + /* Deal with as much of this write request as possible by transferring ** data from the memory mapping using memcpy(). */ if( offsetmmapSize ){ if( offset+amt <= pFile->mmapSize ){ @@ -48248,7 +48720,7 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ ** all references to memory-mapped content are closed. That is doable, ** but involves adding a few branches in the common write code path which ** could slow down normal operations slightly. Hence, we have decided for - ** now to simply make trancations a no-op if there are pending reads. We + ** now to simply make transactions a no-op if there are pending reads. We ** can maybe revisit this decision in the future. */ return SQLITE_OK; @@ -48307,7 +48779,7 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ #ifdef SQLITE_TEST /* ** Count the number of fullsyncs and normal syncs. This is used to test -** that syncs and fullsyncs are occuring at the right times. +** that syncs and fullsyncs are occurring at the right times. */ SQLITE_API int sqlite3_sync_count = 0; SQLITE_API int sqlite3_fullsync_count = 0; @@ -48664,7 +49136,7 @@ static int winLock(sqlite3_file *id, int locktype){ */ if( locktype==EXCLUSIVE_LOCK && res ){ assert( pFile->locktype>=SHARED_LOCK ); - res = winUnlockReadLock(pFile); + (void)winUnlockReadLock(pFile); res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0, SHARED_SIZE, 0); if( res ){ @@ -50068,6 +50540,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; size_t i, j; + DWORD pid; int nPre = sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX); int nMax, nBuf, nDir, nLen; char *zBuf; @@ -50280,7 +50753,10 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ j = sqlite3Strlen30(zBuf); sqlite3_randomness(15, &zBuf[j]); + pid = osGetCurrentProcessId(); for(i=0; i<15; i++, j++){ + zBuf[j] += pid & 0xff; + pid >>= 8; zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; @@ -52645,7 +53121,7 @@ SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec *p, u32 i){ h = BITVEC_HASH(i++); /* if there wasn't a hash collision, and this doesn't */ /* completely fill the hash, then just add it without */ - /* worring about sub-dividing and re-hashing. */ + /* worrying about sub-dividing and re-hashing. */ if( !p->u.aHash[h] ){ if (p->nSet<(BITVEC_NINT-1)) { goto bitvec_set_end; @@ -52978,7 +53454,7 @@ struct PCache { ** Return 1 if pPg is on the dirty list for pCache. Return 0 if not. ** This routine runs inside of assert() statements only. */ -#ifdef SQLITE_DEBUG +#if defined(SQLITE_ENABLE_EXPENSIVE_ASSERT) static int pageOnDirtyList(PCache *pCache, PgHdr *pPg){ PgHdr *p; for(p=pCache->pDirty; p; p=p->pDirtyNext){ @@ -52986,6 +53462,16 @@ static int pageOnDirtyList(PCache *pCache, PgHdr *pPg){ } return 0; } +static int pageNotOnDirtyList(PCache *pCache, PgHdr *pPg){ + PgHdr *p; + for(p=pCache->pDirty; p; p=p->pDirtyNext){ + if( p==pPg ) return 0; + } + return 1; +} +#else +# define pageOnDirtyList(A,B) 1 +# define pageNotOnDirtyList(A,B) 1 #endif /* @@ -53006,7 +53492,7 @@ SQLITE_PRIVATE int sqlite3PcachePageSanity(PgHdr *pPg){ assert( pCache!=0 ); /* Every page has an associated PCache */ if( pPg->flags & PGHDR_CLEAN ){ assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */ - assert( !pageOnDirtyList(pCache, pPg) );/* CLEAN pages not on dirty list */ + assert( pageNotOnDirtyList(pCache, pPg) );/* CLEAN pages not on dirtylist */ }else{ assert( (pPg->flags & PGHDR_DIRTY)!=0 );/* If not CLEAN must be DIRTY */ assert( pPg->pDirtyNext==0 || pPg->pDirtyNext->pDirtyPrev==pPg ); @@ -53142,7 +53628,7 @@ static int numberOfCachePages(PCache *p){ return p->szCache; }else{ i64 n; - /* IMPLEMANTATION-OF: R-59858-46238 If the argument N is negative, then the + /* IMPLEMENTATION-OF: R-59858-46238 If the argument N is negative, then the ** number of cache pages is adjusted to be a number of pages that would ** use approximately abs(N*1024) bytes of memory based on the current ** page size. */ @@ -53630,7 +54116,7 @@ static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){ } /* -** Sort the list of pages in accending order by pgno. Pages are +** Sort the list of pages in ascending order by pgno. Pages are ** connected by pDirty pointers. The pDirtyPrev pointers are ** corrupted by this sort. ** @@ -53870,7 +54356,7 @@ SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHd ** If N is positive, then N pages worth of memory are allocated using a single ** sqlite3Malloc() call and that memory is used for the first N pages allocated. ** Or if N is negative, then -1024*N bytes of memory are allocated and used -** for as many pages as can be accomodated. +** for as many pages as can be accommodated. ** ** Only one of (2) or (3) can be used. Once the memory available to (2) or ** (3) is exhausted, subsequent allocations fail over to the general-purpose @@ -53904,7 +54390,7 @@ typedef struct PGroup PGroup; ** in memory directly after the associated page data, if the database is ** corrupt, code at the b-tree layer may overread the page buffer and ** read part of this structure before the corruption is detected. This -** can cause a valgrind error if the unitialized gap is accessed. Using u16 +** can cause a valgrind error if the uninitialized gap is accessed. Using u16 ** ensures there is no such gap, and therefore no bytes of uninitialized ** memory in the structure. ** @@ -55124,7 +55610,7 @@ SQLITE_PRIVATE void sqlite3PcacheStats( ** The TEST primitive includes a "batch" number. The TEST primitive ** will only see elements that were inserted before the last change ** in the batch number. In other words, if an INSERT occurs between -** two TESTs where the TESTs have the same batch nubmer, then the +** two TESTs where the TESTs have the same batch number, then the ** value added by the INSERT will not be visible to the second TEST. ** The initial batch number is zero, so if the very first TEST contains ** a non-zero batch number, it will see all prior INSERTs. @@ -55656,6 +56142,7 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64 # define sqlite3WalFramesize(z) 0 # define sqlite3WalFindFrame(x,y,z) 0 # define sqlite3WalFile(x) 0 +# undef SQLITE_USE_SEH #else #define WAL_SAVEPOINT_NDATA 4 @@ -55762,6 +56249,10 @@ SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock); SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db); #endif +#ifdef SQLITE_USE_SEH +SQLITE_PRIVATE int sqlite3WalSystemErrno(Wal*); +#endif + #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* SQLITE_WAL_H */ @@ -56047,7 +56538,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ ** outstanding transactions have been abandoned, the pager is able to ** transition back to OPEN state, discarding the contents of the ** page-cache and any other in-memory state at the same time. Everything -** is reloaded from disk (and, if necessary, hot-journal rollback peformed) +** is reloaded from disk (and, if necessary, hot-journal rollback performed) ** when a read-transaction is next opened on the pager (transitioning ** the pager into READER state). At that point the system has recovered ** from the error. @@ -57420,7 +57911,7 @@ static int readJournalHdr( ** + 4 bytes: super-journal name checksum. ** + 8 bytes: aJournalMagic[]. ** -** The super-journal page checksum is the sum of the bytes in thesuper-journal +** The super-journal page checksum is the sum of the bytes in the super-journal ** name, where each byte is interpreted as a signed 8-bit integer. ** ** If zSuper is a NULL pointer (occurs for a single database transaction), @@ -57473,7 +57964,7 @@ static int writeSuperJournal(Pager *pPager, const char *zSuper){ } pPager->journalOff += (nSuper+20); - /* If the pager is in peristent-journal mode, then the physical + /* If the pager is in persistent-journal mode, then the physical ** journal-file may extend past the end of the super-journal name ** and 8 bytes of magic data just written to the file. This is ** dangerous because the code to rollback a hot-journal file @@ -57643,7 +58134,7 @@ static void pager_unlock(Pager *pPager){ /* ** This function is called whenever an IOERR or FULL error that requires -** the pager to transition into the ERROR state may ahve occurred. +** the pager to transition into the ERROR state may have occurred. ** The first argument is a pointer to the pager structure, the second ** the error-code about to be returned by a pager API function. The ** value returned is a copy of the second argument to this function. @@ -57918,7 +58409,7 @@ static void pagerUnlockAndRollback(Pager *pPager){ /* ** Parameter aData must point to a buffer of pPager->pageSize bytes -** of data. Compute and return a checksum based ont the contents of the +** of data. Compute and return a checksum based on the contents of the ** page of data and the current value of pPager->cksumInit. ** ** This is not a real checksum. It is really just the sum of the @@ -58884,7 +59375,7 @@ static int pagerWalFrames( assert( pPager->pWal ); assert( pList ); #ifdef SQLITE_DEBUG - /* Verify that the page list is in accending order */ + /* Verify that the page list is in ascending order */ for(p=pList; p && p->pDirty; p=p->pDirty){ assert( p->pgno < p->pDirty->pgno ); } @@ -59015,7 +59506,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){ #ifndef SQLITE_OMIT_WAL /* ** Check if the *-wal file that corresponds to the database opened by pPager -** exists if the database is not empy, or verify that the *-wal file does +** exists if the database is not empty, or verify that the *-wal file does ** not exist (by deleting it) if the database file is empty. ** ** If the database is not empty and the *-wal file exists, open the pager @@ -60425,11 +60916,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( int rc = SQLITE_OK; /* Return code */ int tempFile = 0; /* True for temp files (incl. in-memory files) */ int memDb = 0; /* True if this is an in-memory file */ -#ifndef SQLITE_OMIT_DESERIALIZE int memJM = 0; /* Memory journal mode */ -#else -# define memJM 0 -#endif int readOnly = 0; /* True if this is a read-only file */ int journalFileSize; /* Bytes to allocate for each journal fd */ char *zPathname = 0; /* Full path to database file */ @@ -60548,12 +61035,13 @@ SQLITE_PRIVATE int sqlite3PagerOpen( ** specific formatting and order of the various filenames, so if the format ** changes here, be sure to change it there as well. */ + assert( SQLITE_PTRSIZE==sizeof(Pager*) ); pPtr = (u8 *)sqlite3MallocZero( ROUND8(sizeof(*pPager)) + /* Pager structure */ ROUND8(pcacheSize) + /* PCache object */ ROUND8(pVfs->szOsFile) + /* The main db file */ journalFileSize * 2 + /* The two journal files */ - sizeof(pPager) + /* Space to hold a pointer */ + SQLITE_PTRSIZE + /* Space to hold a pointer */ 4 + /* Database prefix */ nPathname + 1 + /* database filename */ nUriByte + /* query parameters */ @@ -60574,7 +61062,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( pPager->sjfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; pPager->jfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); - memcpy(pPtr, &pPager, sizeof(pPager)); pPtr += sizeof(pPager); + memcpy(pPtr, &pPager, SQLITE_PTRSIZE); pPtr += SQLITE_PTRSIZE; /* Fill in the Pager.zFilename and pPager.zQueryParam fields */ pPtr += 4; /* Skip zero prefix */ @@ -60628,9 +61116,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( int fout = 0; /* VFS flags returned by xOpen() */ rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout); assert( !memDb ); -#ifndef SQLITE_OMIT_DESERIALIZE pPager->memVfs = memJM = (fout&SQLITE_OPEN_MEMORY)!=0; -#endif readOnly = (fout&SQLITE_OPEN_READONLY)!=0; /* If the file was successfully opened for read/write access, @@ -60767,7 +61253,7 @@ act_like_temp_file: /* ** Return the sqlite3_file for the main database given the name -** of the corresonding WAL or Journal name as passed into +** of the corresponding WAL or Journal name as passed into ** xOpen. */ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){ @@ -63052,7 +63538,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ assert( pPager->eState!=PAGER_ERROR ); pPager->journalMode = (u8)eMode; - /* When transistioning from TRUNCATE or PERSIST to any other journal + /* When transitioning from TRUNCATE or PERSIST to any other journal ** mode except WAL, unless the pager is in locking_mode=exclusive mode, ** delete the journal file. */ @@ -63480,6 +63966,12 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){ } #endif +#ifdef SQLITE_USE_SEH +SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager *pPager){ + return sqlite3WalSystemErrno(pPager->pWal); +} +#endif + #endif /* SQLITE_OMIT_DISKIO */ /************** End of pager.c ***********************************************/ @@ -63770,7 +64262,7 @@ SQLITE_PRIVATE int sqlite3WalTrace = 0; ** ** Technically, the various VFSes are free to implement these locks however ** they see fit. However, compatibility is encouraged so that VFSes can -** interoperate. The standard implemention used on both unix and windows +** interoperate. The standard implementation used on both unix and windows ** is for the index number to indicate a byte offset into the ** WalCkptInfo.aLock[] array in the wal-index header. In other words, all ** locks are on the shm file. The WALINDEX_LOCK_OFFSET constant (which @@ -63846,7 +64338,7 @@ struct WalIndexHdr { ** the mxFrame for that reader. The value READMARK_NOT_USED (0xffffffff) ** for any aReadMark[] means that entry is unused. aReadMark[0] is ** a special case; its value is never used and it exists as a place-holder -** to avoid having to offset aReadMark[] indexs by one. Readers holding +** to avoid having to offset aReadMark[] indexes by one. Readers holding ** WAL_READ_LOCK(0) always ignore the entire WAL and read all content ** directly from the database. ** @@ -64014,7 +64506,15 @@ struct Wal { u32 iReCksum; /* On commit, recalculate checksums from here */ const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ +#ifdef SQLITE_USE_SEH + u32 lockMask; /* Mask of locks held */ + void *pFree; /* Pointer to sqlite3_free() if exception thrown */ + u32 *pWiValue; /* Value to write into apWiData[iWiPg] */ + int iWiPg; /* Write pWiValue into apWiData[iWiPg] */ + int iSysErrno; /* System error code following exception */ +#endif #ifdef SQLITE_DEBUG + int nSehTry; /* Number of nested SEH_TRY{} blocks */ u8 lockError; /* True if a locking error has occurred */ #endif #ifdef SQLITE_ENABLE_SNAPSHOT @@ -64096,6 +64596,113 @@ struct WalIterator { sizeof(ht_slot)*HASHTABLE_NSLOT + HASHTABLE_NPAGE*sizeof(u32) \ ) +/* +** Structured Exception Handling (SEH) is a Windows-specific technique +** for catching exceptions raised while accessing memory-mapped files. +** +** The -DSQLITE_USE_SEH compile-time option means to use SEH to catch and +** deal with system-level errors that arise during WAL -shm file processing. +** Without this compile-time option, any system-level faults that appear +** while accessing the memory-mapped -shm file will cause a process-wide +** signal to be deliver, which will more than likely cause the entire +** process to exit. +*/ +#ifdef SQLITE_USE_SEH +#include + +/* Beginning of a block of code in which an exception might occur */ +# define SEH_TRY __try { \ + assert( walAssertLockmask(pWal) && pWal->nSehTry==0 ); \ + VVA_ONLY(pWal->nSehTry++); + +/* The end of a block of code in which an exception might occur */ +# define SEH_EXCEPT(X) \ + VVA_ONLY(pWal->nSehTry--); \ + assert( pWal->nSehTry==0 ); \ + } __except( sehExceptionFilter(pWal, GetExceptionCode(), GetExceptionInformation() ) ){ X } + +/* Simulate a memory-mapping fault in the -shm file for testing purposes */ +# define SEH_INJECT_FAULT sehInjectFault(pWal) + +/* +** The second argument is the return value of GetExceptionCode() for the +** current exception. Return EXCEPTION_EXECUTE_HANDLER if the exception code +** indicates that the exception may have been caused by accessing the *-shm +** file mapping. Or EXCEPTION_CONTINUE_SEARCH otherwise. +*/ +static int sehExceptionFilter(Wal *pWal, int eCode, EXCEPTION_POINTERS *p){ + VVA_ONLY(pWal->nSehTry--); + if( eCode==EXCEPTION_IN_PAGE_ERROR ){ + if( p && p->ExceptionRecord && p->ExceptionRecord->NumberParameters>=3 ){ + /* From MSDN: For this type of exception, the first element of the + ** ExceptionInformation[] array is a read-write flag - 0 if the exception + ** was thrown while reading, 1 if while writing. The second element is + ** the virtual address being accessed. The "third array element specifies + ** the underlying NTSTATUS code that resulted in the exception". */ + pWal->iSysErrno = (int)p->ExceptionRecord->ExceptionInformation[2]; + } + return EXCEPTION_EXECUTE_HANDLER; + } + return EXCEPTION_CONTINUE_SEARCH; +} + +/* +** If one is configured, invoke the xTestCallback callback with 650 as +** the argument. If it returns true, throw the same exception that is +** thrown by the system if the *-shm file mapping is accessed after it +** has been invalidated. +*/ +static void sehInjectFault(Wal *pWal){ + int res; + assert( pWal->nSehTry>0 ); + + res = sqlite3FaultSim(650); + if( res!=0 ){ + ULONG_PTR aArg[3]; + aArg[0] = 0; + aArg[1] = 0; + aArg[2] = (ULONG_PTR)res; + RaiseException(EXCEPTION_IN_PAGE_ERROR, 0, 3, (const ULONG_PTR*)aArg); + } +} + +/* +** There are two ways to use this macro. To set a pointer to be freed +** if an exception is thrown: +** +** SEH_FREE_ON_ERROR(0, pPtr); +** +** and to cancel the same: +** +** SEH_FREE_ON_ERROR(pPtr, 0); +** +** In the first case, there must not already be a pointer registered to +** be freed. In the second case, pPtr must be the registered pointer. +*/ +#define SEH_FREE_ON_ERROR(X,Y) \ + assert( (X==0 || Y==0) && pWal->pFree==X ); pWal->pFree = Y + +/* +** There are two ways to use this macro. To arrange for pWal->apWiData[iPg] +** to be set to pValue if an exception is thrown: +** +** SEH_SET_ON_ERROR(iPg, pValue); +** +** and to cancel the same: +** +** SEH_SET_ON_ERROR(0, 0); +*/ +#define SEH_SET_ON_ERROR(X,Y) pWal->iWiPg = X; pWal->pWiValue = Y + +#else +# define SEH_TRY VVA_ONLY(pWal->nSehTry++); +# define SEH_EXCEPT(X) VVA_ONLY(pWal->nSehTry--); assert( pWal->nSehTry==0 ); +# define SEH_INJECT_FAULT assert( pWal->nSehTry>0 ); +# define SEH_FREE_ON_ERROR(X,Y) +# define SEH_SET_ON_ERROR(X,Y) +#endif /* ifdef SQLITE_USE_SEH */ + + /* ** Obtain a pointer to the iPage'th page of the wal-index. The wal-index ** is broken into pages of WALINDEX_PGSZ bytes. Wal-index pages are @@ -64168,6 +64775,7 @@ static int walIndexPage( int iPage, /* The page we seek */ volatile u32 **ppPage /* Write the page pointer here */ ){ + SEH_INJECT_FAULT; if( pWal->nWiData<=iPage || (*ppPage = pWal->apWiData[iPage])==0 ){ return walIndexPageRealloc(pWal, iPage, ppPage); } @@ -64179,6 +64787,7 @@ static int walIndexPage( */ static volatile WalCkptInfo *walCkptInfo(Wal *pWal){ assert( pWal->nWiData>0 && pWal->apWiData[0] ); + SEH_INJECT_FAULT; return (volatile WalCkptInfo*)&(pWal->apWiData[0][sizeof(WalIndexHdr)/2]); } @@ -64187,6 +64796,7 @@ static volatile WalCkptInfo *walCkptInfo(Wal *pWal){ */ static volatile WalIndexHdr *walIndexHdr(Wal *pWal){ assert( pWal->nWiData>0 && pWal->apWiData[0] ); + SEH_INJECT_FAULT; return (volatile WalIndexHdr*)pWal->apWiData[0]; } @@ -64376,7 +64986,7 @@ static int walDecodeFrame( return 0; } - /* A frame is only valid if the page number is creater than zero. + /* A frame is only valid if the page number is greater than zero. */ pgno = sqlite3Get4byte(&aFrame[0]); if( pgno==0 ){ @@ -64384,7 +64994,7 @@ static int walDecodeFrame( } /* A frame is only valid if a checksum of the WAL header, - ** all prior frams, the first 16 bytes of this frame-header, + ** all prior frames, the first 16 bytes of this frame-header, ** and the frame-data matches the checksum in the last 8 ** bytes of this frame-header. */ @@ -64444,12 +65054,18 @@ static int walLockShared(Wal *pWal, int lockIdx){ WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal, walLockName(lockIdx), rc ? "failed" : "ok")); VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) +#ifdef SQLITE_USE_SEH + if( rc==SQLITE_OK ) pWal->lockMask |= (1 << lockIdx); +#endif return rc; } static void walUnlockShared(Wal *pWal, int lockIdx){ if( pWal->exclusiveMode ) return; (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED); +#ifdef SQLITE_USE_SEH + pWal->lockMask &= ~(1 << lockIdx); +#endif WALTRACE(("WAL%p: release SHARED-%s\n", pWal, walLockName(lockIdx))); } static int walLockExclusive(Wal *pWal, int lockIdx, int n){ @@ -64460,12 +65076,20 @@ static int walLockExclusive(Wal *pWal, int lockIdx, int n){ WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, walLockName(lockIdx), n, rc ? "failed" : "ok")); VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) +#ifdef SQLITE_USE_SEH + if( rc==SQLITE_OK ){ + pWal->lockMask |= (((1<exclusiveMode ) return; (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE); +#ifdef SQLITE_USE_SEH + pWal->lockMask &= ~(((1<apWiData[0][WALINDEX_HDR_SIZE/sizeof(u32) + iFrame - 1]; } @@ -64816,6 +65441,7 @@ static int walIndexRecover(Wal *pWal){ /* Malloc a buffer to read frames into. */ szFrame = szPage + WAL_FRAME_HDRSIZE; aFrame = (u8 *)sqlite3_malloc64(szFrame + WALINDEX_PGSZ); + SEH_FREE_ON_ERROR(0, aFrame); if( !aFrame ){ rc = SQLITE_NOMEM_BKPT; goto recovery_error; @@ -64834,6 +65460,7 @@ static int walIndexRecover(Wal *pWal){ rc = walIndexPage(pWal, iPg, (volatile u32**)&aShare); assert( aShare!=0 || rc!=SQLITE_OK ); if( aShare==0 ) break; + SEH_SET_ON_ERROR(iPg, aShare); pWal->apWiData[iPg] = aPrivate; for(iFrame=iFirst; iFrame<=iLast; iFrame++){ @@ -64861,6 +65488,7 @@ static int walIndexRecover(Wal *pWal){ } } pWal->apWiData[iPg] = aShare; + SEH_SET_ON_ERROR(0,0); nHdr = (iPg==0 ? WALINDEX_HDR_SIZE : 0); nHdr32 = nHdr / sizeof(u32); #ifndef SQLITE_SAFER_WALINDEX_RECOVERY @@ -64891,9 +65519,11 @@ static int walIndexRecover(Wal *pWal){ } } #endif + SEH_INJECT_FAULT; if( iFrame<=iLast ) break; } + SEH_FREE_ON_ERROR(aFrame, 0); sqlite3_free(aFrame); } @@ -64921,6 +65551,7 @@ finished: }else{ pInfo->aReadMark[i] = READMARK_NOT_USED; } + SEH_INJECT_FAULT; walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); }else if( rc!=SQLITE_BUSY ){ goto recovery_error; @@ -65078,7 +65709,7 @@ SQLITE_PRIVATE int sqlite3WalOpen( } /* -** Change the size to which the WAL file is trucated on each reset. +** Change the size to which the WAL file is truncated on each reset. */ SQLITE_PRIVATE void sqlite3WalLimit(Wal *pWal, i64 iLimit){ if( pWal ) pWal->mxWalSize = iLimit; @@ -65304,23 +65935,16 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ nByte = sizeof(WalIterator) + (nSegment-1)*sizeof(struct WalSegment) + iLast*sizeof(ht_slot); - p = (WalIterator *)sqlite3_malloc64(nByte); + p = (WalIterator *)sqlite3_malloc64(nByte + + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) + ); if( !p ){ return SQLITE_NOMEM_BKPT; } memset(p, 0, nByte); p->nSegment = nSegment; - - /* Allocate temporary space used by the merge-sort routine. This block - ** of memory will be freed before this function returns. - */ - aTmp = (ht_slot *)sqlite3_malloc64( - sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) - ); - if( !aTmp ){ - rc = SQLITE_NOMEM_BKPT; - } - + aTmp = (ht_slot*)&(((u8*)p)[nByte]); + SEH_FREE_ON_ERROR(0, p); for(i=walFramePage(nBackfill+1); rc==SQLITE_OK && iaSegment[i].aPgno = (u32 *)sLoc.aPgno; } } - sqlite3_free(aTmp); - if( rc!=SQLITE_OK ){ + SEH_FREE_ON_ERROR(p, 0); walIteratorFree(p); p = 0; } @@ -65576,13 +66199,13 @@ static int walCheckpoint( mxSafeFrame = pWal->hdr.mxFrame; mxPage = pWal->hdr.nPage; for(i=1; iaReadMark+i); + u32 y = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT; if( mxSafeFrame>y ){ assert( y<=pWal->hdr.mxFrame ); rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); if( rc==SQLITE_OK ){ u32 iMark = (i==1 ? mxSafeFrame : READMARK_NOT_USED); - AtomicStore(pInfo->aReadMark+i, iMark); + AtomicStore(pInfo->aReadMark+i, iMark); SEH_INJECT_FAULT; walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); }else if( rc==SQLITE_BUSY ){ mxSafeFrame = y; @@ -65603,8 +66226,7 @@ static int walCheckpoint( && (rc = walBusyLock(pWal,xBusy,pBusyArg,WAL_READ_LOCK(0),1))==SQLITE_OK ){ u32 nBackfill = pInfo->nBackfill; - - pInfo->nBackfillAttempted = mxSafeFrame; + pInfo->nBackfillAttempted = mxSafeFrame; SEH_INJECT_FAULT; /* Sync the WAL to disk */ rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags)); @@ -65635,6 +66257,7 @@ static int walCheckpoint( while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ i64 iOffset; assert( walFramePgno(pWal, iFrame)==iDbpage ); + SEH_INJECT_FAULT; if( AtomicLoad(&db->u1.isInterrupted) ){ rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; break; @@ -65664,7 +66287,7 @@ static int walCheckpoint( } } if( rc==SQLITE_OK ){ - AtomicStore(&pInfo->nBackfill, mxSafeFrame); + AtomicStore(&pInfo->nBackfill, mxSafeFrame); SEH_INJECT_FAULT; } } @@ -65686,6 +66309,7 @@ static int walCheckpoint( */ if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){ assert( pWal->writeLock ); + SEH_INJECT_FAULT; if( pInfo->nBackfillhdr.mxFrame ){ rc = SQLITE_BUSY; }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){ @@ -65717,6 +66341,7 @@ static int walCheckpoint( } walcheckpoint_out: + SEH_FREE_ON_ERROR(pIter, 0); walIteratorFree(pIter); return rc; } @@ -65739,6 +66364,93 @@ static void walLimitSize(Wal *pWal, i64 nMax){ } } +#ifdef SQLITE_USE_SEH +/* +** This is the "standard" exception handler used in a few places to handle +** an exception thrown by reading from the *-shm mapping after it has become +** invalid in SQLITE_USE_SEH builds. It is used as follows: +** +** SEH_TRY { ... } +** SEH_EXCEPT( rc = walHandleException(pWal); ) +** +** This function does three things: +** +** 1) Determines the locks that should be held, based on the contents of +** the Wal.readLock, Wal.writeLock and Wal.ckptLock variables. All other +** held locks are assumed to be transient locks that would have been +** released had the exception not been thrown and are dropped. +** +** 2) Frees the pointer at Wal.pFree, if any, using sqlite3_free(). +** +** 3) Set pWal->apWiData[pWal->iWiPg] to pWal->pWiValue if not NULL +** +** 4) Returns SQLITE_IOERR. +*/ +static int walHandleException(Wal *pWal){ + if( pWal->exclusiveMode==0 ){ + static const int S = 1; + static const int E = (1<lockMask & ~( + (pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock))) + | (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0) + | (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0) + ); + for(ii=0; iipFree); + pWal->pFree = 0; + if( pWal->pWiValue ){ + pWal->apWiData[pWal->iWiPg] = pWal->pWiValue; + pWal->pWiValue = 0; + } + return SQLITE_IOERR_IN_PAGE; +} + +/* +** Assert that the Wal.lockMask mask, which indicates the locks held +** by the connenction, is consistent with the Wal.readLock, Wal.writeLock +** and Wal.ckptLock variables. To be used as: +** +** assert( walAssertLockmask(pWal) ); +*/ +static int walAssertLockmask(Wal *pWal){ + if( pWal->exclusiveMode==0 ){ + static const int S = 1; + static const int E = (1<readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock))) + | (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0) + | (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0) +#ifdef SQLITE_ENABLE_SNAPSHOT + | (pWal->pSnapshot ? (pWal->lockMask & (1 << WAL_CKPT_LOCK)) : 0) +#endif + ); + assert( mExpect==pWal->lockMask ); + } + return 1; +} + +/* +** Return and zero the "system error" field set when an +** EXCEPTION_IN_PAGE_ERROR exception is caught. +*/ +SQLITE_PRIVATE int sqlite3WalSystemErrno(Wal *pWal){ + int iRet = 0; + if( pWal ){ + iRet = pWal->iSysErrno; + pWal->iSysErrno = 0; + } + return iRet; +} + +#else +# define walAssertLockmask(x) 1 +#endif /* ifdef SQLITE_USE_SEH */ + /* ** Close a connection to a log file. */ @@ -65753,6 +66465,8 @@ SQLITE_PRIVATE int sqlite3WalClose( if( pWal ){ int isDelete = 0; /* True to unlink wal and wal-index files */ + assert( walAssertLockmask(pWal) ); + /* If an EXCLUSIVE lock can be obtained on the database file (using the ** ordinary, rollback-mode locking methods, this guarantees that the ** connection associated with this log file is the only connection to @@ -65777,7 +66491,7 @@ SQLITE_PRIVATE int sqlite3WalClose( ); if( bPersist!=1 ){ /* Try to delete the WAL file if the checkpoint completed and - ** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal + ** fsynced (rc==SQLITE_OK) and if we are not in persistent-wal ** mode (!bPersist) */ isDelete = 1; }else if( pWal->mxWalSize>=0 ){ @@ -65844,7 +66558,7 @@ static SQLITE_NO_TSAN int walIndexTryHdr(Wal *pWal, int *pChanged){ ** give false-positive warnings about these accesses because the tools do not ** account for the double-read and the memory barrier. The use of mutexes ** here would be problematic as the memory being accessed is potentially - ** shared among multiple processes and not all mutex implementions work + ** shared among multiple processes and not all mutex implementations work ** reliably in that environment. */ aHdr = walIndexHdr(pWal); @@ -66295,6 +67009,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ assert( pWal->nWiData>0 ); assert( pWal->apWiData[0]!=0 ); pInfo = walCkptInfo(pWal); + SEH_INJECT_FAULT; if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame #ifdef SQLITE_ENABLE_SNAPSHOT && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0) @@ -66344,7 +67059,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ } #endif for(i=1; iaReadMark+i); + u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT; if( mxReadMark<=thisMark && thisMark<=mxFrame ){ assert( thisMark!=READMARK_NOT_USED ); mxReadMark = thisMark; @@ -66410,7 +67125,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ ** we can guarantee that the checkpointer that set nBackfill could not ** see any pages past pWal->hdr.mxFrame, this problem does not come up. */ - pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; + pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT; walShmBarrier(pWal); if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) @@ -66425,6 +67140,54 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ } #ifdef SQLITE_ENABLE_SNAPSHOT +/* +** This function does the work of sqlite3WalSnapshotRecover(). +*/ +static int walSnapshotRecover( + Wal *pWal, /* WAL handle */ + void *pBuf1, /* Temp buffer pWal->szPage bytes in size */ + void *pBuf2 /* Temp buffer pWal->szPage bytes in size */ +){ + int szPage = (int)pWal->szPage; + int rc; + i64 szDb; /* Size of db file in bytes */ + + rc = sqlite3OsFileSize(pWal->pDbFd, &szDb); + if( rc==SQLITE_OK ){ + volatile WalCkptInfo *pInfo = walCkptInfo(pWal); + u32 i = pInfo->nBackfillAttempted; + for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){ + WalHashLoc sLoc; /* Hash table location */ + u32 pgno; /* Page number in db file */ + i64 iDbOff; /* Offset of db file entry */ + i64 iWalOff; /* Offset of wal file entry */ + + rc = walHashGet(pWal, walFramePage(i), &sLoc); + if( rc!=SQLITE_OK ) break; + assert( i - sLoc.iZero - 1 >=0 ); + pgno = sLoc.aPgno[i-sLoc.iZero-1]; + iDbOff = (i64)(pgno-1) * szPage; + + if( iDbOff+szPage<=szDb ){ + iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE; + rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff); + + if( rc==SQLITE_OK ){ + rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff); + } + + if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){ + break; + } + } + + pInfo->nBackfillAttempted = i-1; + } + } + + return rc; +} + /* ** Attempt to reduce the value of the WalCkptInfo.nBackfillAttempted ** variable so that older snapshots can be accessed. To do this, loop @@ -66450,50 +67213,21 @@ SQLITE_PRIVATE int sqlite3WalSnapshotRecover(Wal *pWal){ assert( pWal->readLock>=0 ); rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); if( rc==SQLITE_OK ){ - volatile WalCkptInfo *pInfo = walCkptInfo(pWal); - int szPage = (int)pWal->szPage; - i64 szDb; /* Size of db file in bytes */ - - rc = sqlite3OsFileSize(pWal->pDbFd, &szDb); - if( rc==SQLITE_OK ){ - void *pBuf1 = sqlite3_malloc(szPage); - void *pBuf2 = sqlite3_malloc(szPage); - if( pBuf1==0 || pBuf2==0 ){ - rc = SQLITE_NOMEM; - }else{ - u32 i = pInfo->nBackfillAttempted; - for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){ - WalHashLoc sLoc; /* Hash table location */ - u32 pgno; /* Page number in db file */ - i64 iDbOff; /* Offset of db file entry */ - i64 iWalOff; /* Offset of wal file entry */ - - rc = walHashGet(pWal, walFramePage(i), &sLoc); - if( rc!=SQLITE_OK ) break; - assert( i - sLoc.iZero - 1 >=0 ); - pgno = sLoc.aPgno[i-sLoc.iZero-1]; - iDbOff = (i64)(pgno-1) * szPage; - - if( iDbOff+szPage<=szDb ){ - iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE; - rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff); - - if( rc==SQLITE_OK ){ - rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff); - } - - if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){ - break; - } - } - - pInfo->nBackfillAttempted = i-1; - } + void *pBuf1 = sqlite3_malloc(pWal->szPage); + void *pBuf2 = sqlite3_malloc(pWal->szPage); + if( pBuf1==0 || pBuf2==0 ){ + rc = SQLITE_NOMEM; + }else{ + pWal->ckptLock = 1; + SEH_TRY { + rc = walSnapshotRecover(pWal, pBuf1, pBuf2); } - - sqlite3_free(pBuf1); - sqlite3_free(pBuf2); + SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) + pWal->ckptLock = 0; } + + sqlite3_free(pBuf1); + sqlite3_free(pBuf2); walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); } @@ -66502,28 +67236,20 @@ SQLITE_PRIVATE int sqlite3WalSnapshotRecover(Wal *pWal){ #endif /* SQLITE_ENABLE_SNAPSHOT */ /* -** Begin a read transaction on the database. -** -** This routine used to be called sqlite3OpenSnapshot() and with good reason: -** it takes a snapshot of the state of the WAL and wal-index for the current -** instant in time. The current thread will continue to use this snapshot. -** Other threads might append new content to the WAL and wal-index but -** that extra content is ignored by the current thread. -** -** If the database contents have changes since the previous read -** transaction, then *pChanged is set to 1 before returning. The -** Pager layer will use this to know that its cache is stale and -** needs to be flushed. +** This function does the work of sqlite3WalBeginReadTransaction() (see +** below). That function simply calls this one inside an SEH_TRY{...} block. */ -SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ +static int walBeginReadTransaction(Wal *pWal, int *pChanged){ int rc; /* Return code */ int cnt = 0; /* Number of TryBeginRead attempts */ #ifdef SQLITE_ENABLE_SNAPSHOT + int ckptLock = 0; int bChanged = 0; WalIndexHdr *pSnapshot = pWal->pSnapshot; #endif assert( pWal->ckptLock==0 ); + assert( pWal->nSehTry>0 ); #ifdef SQLITE_ENABLE_SNAPSHOT if( pSnapshot ){ @@ -66546,7 +67272,7 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ if( rc!=SQLITE_OK ){ return rc; } - pWal->ckptLock = 1; + ckptLock = 1; } #endif @@ -66610,15 +67336,37 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ } /* Release the shared CKPT lock obtained above. */ - if( pWal->ckptLock ){ + if( ckptLock ){ assert( pSnapshot ); walUnlockShared(pWal, WAL_CKPT_LOCK); - pWal->ckptLock = 0; } #endif return rc; } +/* +** Begin a read transaction on the database. +** +** This routine used to be called sqlite3OpenSnapshot() and with good reason: +** it takes a snapshot of the state of the WAL and wal-index for the current +** instant in time. The current thread will continue to use this snapshot. +** Other threads might append new content to the WAL and wal-index but +** that extra content is ignored by the current thread. +** +** If the database contents have changes since the previous read +** transaction, then *pChanged is set to 1 before returning. The +** Pager layer will use this to know that its cache is stale and +** needs to be flushed. +*/ +SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ + int rc; + SEH_TRY { + rc = walBeginReadTransaction(pWal, pChanged); + } + SEH_EXCEPT( rc = walHandleException(pWal); ) + return rc; +} + /* ** Finish with a read transaction. All this does is release the ** read-lock. @@ -66639,7 +67387,7 @@ SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){ ** Return SQLITE_OK if successful, or an error code if an error occurs. If an ** error does occur, the final value of *piRead is undefined. */ -SQLITE_PRIVATE int sqlite3WalFindFrame( +static int walFindFrame( Wal *pWal, /* WAL handle */ Pgno pgno, /* Database page number to read data for */ u32 *piRead /* OUT: Frame number (or zero) */ @@ -66702,6 +67450,7 @@ SQLITE_PRIVATE int sqlite3WalFindFrame( } nCollide = HASHTABLE_NSLOT; iKey = walHash(pgno); + SEH_INJECT_FAULT; while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){ u32 iFrame = iH + sLoc.iZero; if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){ @@ -66738,6 +67487,30 @@ SQLITE_PRIVATE int sqlite3WalFindFrame( return SQLITE_OK; } +/* +** Search the wal file for page pgno. If found, set *piRead to the frame that +** contains the page. Otherwise, if pgno is not in the wal file, set *piRead +** to zero. +** +** Return SQLITE_OK if successful, or an error code if an error occurs. If an +** error does occur, the final value of *piRead is undefined. +** +** The difference between this function and walFindFrame() is that this +** function wraps walFindFrame() in an SEH_TRY{...} block. +*/ +SQLITE_PRIVATE int sqlite3WalFindFrame( + Wal *pWal, /* WAL handle */ + Pgno pgno, /* Database page number to read data for */ + u32 *piRead /* OUT: Frame number (or zero) */ +){ + int rc; + SEH_TRY { + rc = walFindFrame(pWal, pgno, piRead); + } + SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) + return rc; +} + /* ** Read the contents of frame iRead from the wal file into buffer pOut ** (which is nOut bytes in size). Return SQLITE_OK if successful, or an @@ -66819,12 +67592,17 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){ ** time the read transaction on this connection was started, then ** the write is disallowed. */ - if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){ + SEH_TRY { + if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){ + rc = SQLITE_BUSY_SNAPSHOT; + } + } + SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) + + if( rc!=SQLITE_OK ){ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); pWal->writeLock = 0; - rc = SQLITE_BUSY_SNAPSHOT; } - return rc; } @@ -66860,30 +67638,33 @@ SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *p Pgno iMax = pWal->hdr.mxFrame; Pgno iFrame; - /* Restore the clients cache of the wal-index header to the state it - ** was in before the client began writing to the database. - */ - memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr)); - - for(iFrame=pWal->hdr.mxFrame+1; - ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; - iFrame++ - ){ - /* This call cannot fail. Unless the page for which the page number - ** is passed as the second argument is (a) in the cache and - ** (b) has an outstanding reference, then xUndo is either a no-op - ** (if (a) is false) or simply expels the page from the cache (if (b) - ** is false). - ** - ** If the upper layer is doing a rollback, it is guaranteed that there - ** are no outstanding references to any page other than page 1. And - ** page 1 is never written to the log until the transaction is - ** committed. As a result, the call to xUndo may not fail. + SEH_TRY { + /* Restore the clients cache of the wal-index header to the state it + ** was in before the client began writing to the database. */ - assert( walFramePgno(pWal, iFrame)!=1 ); - rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame)); + memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr)); + + for(iFrame=pWal->hdr.mxFrame+1; + ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; + iFrame++ + ){ + /* This call cannot fail. Unless the page for which the page number + ** is passed as the second argument is (a) in the cache and + ** (b) has an outstanding reference, then xUndo is either a no-op + ** (if (a) is false) or simply expels the page from the cache (if (b) + ** is false). + ** + ** If the upper layer is doing a rollback, it is guaranteed that there + ** are no outstanding references to any page other than page 1. And + ** page 1 is never written to the log until the transaction is + ** committed. As a result, the call to xUndo may not fail. + */ + assert( walFramePgno(pWal, iFrame)!=1 ); + rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame)); + } + if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal); } - if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal); + SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) } return rc; } @@ -66927,7 +67708,10 @@ SQLITE_PRIVATE int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){ pWal->hdr.mxFrame = aWalData[0]; pWal->hdr.aFrameCksum[0] = aWalData[1]; pWal->hdr.aFrameCksum[1] = aWalData[2]; - walCleanupHash(pWal); + SEH_TRY { + walCleanupHash(pWal); + } + SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) } return rc; @@ -67108,7 +67892,7 @@ static int walRewriteChecksums(Wal *pWal, u32 iLast){ ** Write a set of frames to the log. The caller must hold the write-lock ** on the log file (obtained using sqlite3WalBeginWriteTransaction()). */ -SQLITE_PRIVATE int sqlite3WalFrames( +static int walFrames( Wal *pWal, /* Wal handle to write to */ int szPage, /* Database page-size in bytes */ PgHdr *pList, /* List of dirty pages to write */ @@ -67219,7 +68003,7 @@ SQLITE_PRIVATE int sqlite3WalFrames( ** checksums must be recomputed when the transaction is committed. */ if( iFirst && (p->pDirty || isCommit==0) ){ u32 iWrite = 0; - VVA_ONLY(rc =) sqlite3WalFindFrame(pWal, p->pgno, &iWrite); + VVA_ONLY(rc =) walFindFrame(pWal, p->pgno, &iWrite); assert( rc==SQLITE_OK || iWrite==0 ); if( iWrite>=iFirst ){ i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE; @@ -67338,6 +68122,29 @@ SQLITE_PRIVATE int sqlite3WalFrames( return rc; } +/* +** Write a set of frames to the log. The caller must hold the write-lock +** on the log file (obtained using sqlite3WalBeginWriteTransaction()). +** +** The difference between this function and walFrames() is that this +** function wraps walFrames() in an SEH_TRY{...} block. +*/ +SQLITE_PRIVATE int sqlite3WalFrames( + Wal *pWal, /* Wal handle to write to */ + int szPage, /* Database page-size in bytes */ + PgHdr *pList, /* List of dirty pages to write */ + Pgno nTruncate, /* Database size after this commit */ + int isCommit, /* True if this is a commit */ + int sync_flags /* Flags to pass to OsSync() (or 0) */ +){ + int rc; + SEH_TRY { + rc = walFrames(pWal, szPage, pList, nTruncate, isCommit, sync_flags); + } + SEH_EXCEPT( rc = walHandleException(pWal); ) + return rc; +} + /* ** This routine is called to implement sqlite3_wal_checkpoint() and ** related interfaces. @@ -67417,30 +68224,33 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( /* Read the wal-index header. */ - if( rc==SQLITE_OK ){ - walDisableBlocking(pWal); - rc = walIndexReadHdr(pWal, &isChanged); - (void)walEnableBlocking(pWal); - if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ - sqlite3OsUnfetch(pWal->pDbFd, 0, 0); - } - } - - /* Copy data from the log to the database file. */ - if( rc==SQLITE_OK ){ - - if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ - rc = SQLITE_CORRUPT_BKPT; - }else{ - rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags, zBuf); - } - - /* If no error occurred, set the output variables. */ - if( rc==SQLITE_OK || rc==SQLITE_BUSY ){ - if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; - if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill); + SEH_TRY { + if( rc==SQLITE_OK ){ + walDisableBlocking(pWal); + rc = walIndexReadHdr(pWal, &isChanged); + (void)walEnableBlocking(pWal); + if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ + sqlite3OsUnfetch(pWal->pDbFd, 0, 0); + } + } + + /* Copy data from the log to the database file. */ + if( rc==SQLITE_OK ){ + if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags,zBuf); + } + + /* If no error occurred, set the output variables. */ + if( rc==SQLITE_OK || rc==SQLITE_BUSY ){ + if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; + SEH_INJECT_FAULT; + if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill); + } } } + SEH_EXCEPT( rc = walHandleException(pWal); ) if( isChanged ){ /* If a new wal-index header was loaded before the checkpoint was @@ -67517,7 +68327,9 @@ SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op){ ** locks are taken in this case). Nor should the pager attempt to ** upgrade to exclusive-mode following such an error. */ +#ifndef SQLITE_USE_SEH assert( pWal->readLock>=0 || pWal->lockError ); +#endif assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) ); if( op==0 ){ @@ -67618,16 +68430,19 @@ SQLITE_API int sqlite3_snapshot_cmp(sqlite3_snapshot *p1, sqlite3_snapshot *p2){ */ SQLITE_PRIVATE int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot){ int rc; - rc = walLockShared(pWal, WAL_CKPT_LOCK); - if( rc==SQLITE_OK ){ - WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot; - if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) - || pNew->mxFramenBackfillAttempted - ){ - rc = SQLITE_ERROR_SNAPSHOT; - walUnlockShared(pWal, WAL_CKPT_LOCK); + SEH_TRY { + rc = walLockShared(pWal, WAL_CKPT_LOCK); + if( rc==SQLITE_OK ){ + WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot; + if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) + || pNew->mxFramenBackfillAttempted + ){ + rc = SQLITE_ERROR_SNAPSHOT; + walUnlockShared(pWal, WAL_CKPT_LOCK); + } } } + SEH_EXCEPT( rc = walHandleException(pWal); ) return rc; } @@ -67866,7 +68681,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){ ** 0x81 0x00 becomes 0x00000080 ** 0x82 0x00 becomes 0x00000100 ** 0x80 0x7f becomes 0x0000007f -** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678 +** 0x81 0x91 0xd1 0xac 0x78 becomes 0x12345678 ** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081 ** ** Variable length integers are used for rowids and to hold the number of @@ -67949,7 +68764,7 @@ typedef struct CellInfo CellInfo; ** page that has been loaded into memory. The information in this object ** is derived from the raw on-disk page content. ** -** As each database page is loaded into memory, the pager allocats an +** As each database page is loaded into memory, the pager allocates an ** instance of this object and zeros the first 8 bytes. (This is the ** "extra" information associated with each page of the pager.) ** @@ -68405,7 +69220,7 @@ struct IntegrityCk { /* ** get2byteAligned(), unlike get2byte(), requires that its argument point to a -** two-byte aligned address. get2bytea() is only used for accessing the +** two-byte aligned address. get2byteAligned() is only used for accessing the ** cell addresses in a btree header. */ #if SQLITE_BYTEORDER==4321 @@ -68582,7 +69397,7 @@ SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree *p){ ** ** There is a corresponding leave-all procedures. ** -** Enter the mutexes in accending order by BtShared pointer address +** Enter the mutexes in ascending order by BtShared pointer address ** to avoid the possibility of deadlock when two threads with ** two or more btrees in common both try to lock all their btrees ** at the same instant. @@ -68714,6 +69529,7 @@ SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){ /************** End of btmutex.c *********************************************/ /************** Begin file btree.c *******************************************/ + /* ** 2004 April 6 ** @@ -70249,7 +71065,7 @@ static void ptrmapPutOvflPtr(MemPage *pPage, MemPage *pSrc, u8 *pCell,int *pRC){ pPage->xParseCell(pPage, pCell, &info); if( info.nLocalaDataEnd, pCell, pCell+info.nLocal) ){ + if( SQLITE_OVERFLOW(pSrc->aDataEnd, pCell, pCell+info.nLocal) ){ testcase( pSrc!=pPage ); *pRC = SQLITE_CORRUPT_BKPT; return; @@ -70350,7 +71166,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ iCellStart = get2byte(&data[hdr+5]); if( nCell>0 ){ temp = sqlite3PagerTempSpace(pPage->pBt->pPager); - memcpy(&temp[iCellStart], &data[iCellStart], usableSize - iCellStart); + memcpy(temp, data, usableSize); src = temp; for(i=0; iiPage. -** -** The page is fetched as read-write unless pCur is not NULL and is -** a read-only cursor. -** -** If an error occurs, then *ppPage is undefined. It -** may remain unchanged, or it may be set to an invalid value. */ static int getAndInitPage( BtShared *pBt, /* The database file */ Pgno pgno, /* Number of the page to get */ MemPage **ppPage, /* Write the page pointer here */ - BtCursor *pCur, /* Cursor to receive the page, or NULL */ int bReadOnly /* True for a read-only page */ ){ int rc; DbPage *pDbPage; + MemPage *pPage; assert( sqlite3_mutex_held(pBt->mutex) ); - assert( pCur==0 || ppPage==&pCur->pPage ); - assert( pCur==0 || bReadOnly==pCur->curPagerFlags ); - assert( pCur==0 || pCur->iPage>0 ); if( pgno>btreePagecount(pBt) ){ - rc = SQLITE_CORRUPT_BKPT; - goto getAndInitPage_error1; + *ppPage = 0; + return SQLITE_CORRUPT_BKPT; } rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly); if( rc ){ - goto getAndInitPage_error1; + *ppPage = 0; + return rc; } - *ppPage = (MemPage*)sqlite3PagerGetExtra(pDbPage); - if( (*ppPage)->isInit==0 ){ + pPage = (MemPage*)sqlite3PagerGetExtra(pDbPage); + if( pPage->isInit==0 ){ btreePageFromDbPage(pDbPage, pgno, pBt); - rc = btreeInitPage(*ppPage); + rc = btreeInitPage(pPage); if( rc!=SQLITE_OK ){ - goto getAndInitPage_error2; + releasePage(pPage); + *ppPage = 0; + return rc; } } - assert( (*ppPage)->pgno==pgno || CORRUPT_DB ); - assert( (*ppPage)->aData==sqlite3PagerGetData(pDbPage) ); - - /* If obtaining a child page for a cursor, we must verify that the page is - ** compatible with the root page. */ - if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){ - rc = SQLITE_CORRUPT_PGNO(pgno); - goto getAndInitPage_error2; - } + assert( pPage->pgno==pgno || CORRUPT_DB ); + assert( pPage->aData==sqlite3PagerGetData(pDbPage) ); + *ppPage = pPage; return SQLITE_OK; - -getAndInitPage_error2: - releasePage(*ppPage); -getAndInitPage_error1: - if( pCur ){ - pCur->iPage--; - pCur->pPage = pCur->apPage[pCur->iPage]; - } - testcase( pgno==0 ); - assert( pgno!=0 || rc!=SQLITE_OK ); - return rc; } /* @@ -71177,7 +71966,7 @@ static void pageReinit(DbPage *pData){ ** call to btreeInitPage() will likely return SQLITE_CORRUPT. ** But no harm is done by this. And it is very important that ** btreeInitPage() be called on every btree page so we make - ** the call for every page that comes in for re-initing. */ + ** the call for every page that comes in for re-initializing. */ btreeInitPage(pPage); } } @@ -71356,6 +72145,9 @@ SQLITE_PRIVATE int sqlite3BtreeOpen( assert( sizeof(u16)==2 ); assert( sizeof(Pgno)==4 ); + /* Suppress false-positive compiler warning from PVS-Studio */ + memset(&zDbHeader[16], 0, 8); + pBt = sqlite3MallocZero( sizeof(*pBt) ); if( pBt==0 ){ rc = SQLITE_NOMEM_BKPT; @@ -71572,7 +72364,7 @@ static SQLITE_NOINLINE int allocateTempSpace(BtShared *pBt){ ** can mean that fillInCell() only initializes the first 2 or 3 ** bytes of pTmpSpace, but that the first 4 bytes are copied from ** it into a database page. This is not actually a problem, but it - ** does cause a valgrind error when the 1 or 2 bytes of unitialized + ** does cause a valgrind error when the 1 or 2 bytes of uninitialized ** data is passed to system call write(). So to avoid this error, ** zero the first 4 bytes of temp space here. ** @@ -71807,7 +72599,7 @@ SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p){ /* ** Return the number of bytes of space at the end of every page that -** are intentually left unused. This is the "reserved" space that is +** are intentionally left unused. This is the "reserved" space that is ** sometimes used by extensions. ** ** The value returned is the larger of the current reserve size and @@ -72054,7 +72846,6 @@ static int lockBtree(BtShared *pBt){ ){ goto page1_init_failed; } - pBt->btsFlags |= BTS_PAGESIZE_FIXED; assert( (pageSize & 7)==0 ); /* EVIDENCE-OF: R-59310-51205 The "reserved space" size in the 1-byte ** integer at offset 20 is the number of bytes of space at the end of @@ -72074,6 +72865,7 @@ static int lockBtree(BtShared *pBt){ releasePageOne(pPage1); pBt->usableSize = usableSize; pBt->pageSize = pageSize; + pBt->btsFlags |= BTS_PAGESIZE_FIXED; freeTempSpace(pBt); rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, pageSize-usableSize); @@ -72093,6 +72885,7 @@ static int lockBtree(BtShared *pBt){ if( usableSize<480 ){ goto page1_init_failed; } + pBt->btsFlags |= BTS_PAGESIZE_FIXED; pBt->pageSize = pageSize; pBt->usableSize = usableSize; #ifndef SQLITE_OMIT_AUTOVACUUM @@ -72271,7 +73064,11 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p){ ** when A already has a read lock, we encourage A to give up and let B ** proceed. */ -SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){ +static SQLITE_NOINLINE int btreeBeginTrans( + Btree *p, /* The btree in which to start the transaction */ + int wrflag, /* True to start a write transaction */ + int *pSchemaVersion /* Put schema version number here, if not NULL */ +){ BtShared *pBt = p->pBt; Pager *pPager = pBt->pPager; int rc = SQLITE_OK; @@ -72443,6 +73240,28 @@ trans_begun: sqlite3BtreeLeave(p); return rc; } +SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){ + BtShared *pBt; + if( p->sharable + || p->inTrans==TRANS_NONE + || (p->inTrans==TRANS_READ && wrflag!=0) + ){ + return btreeBeginTrans(p,wrflag,pSchemaVersion); + } + pBt = p->pBt; + if( pSchemaVersion ){ + *pSchemaVersion = get4byte(&pBt->pPage1->aData[40]); + } + if( wrflag ){ + /* This call makes sure that the pager has the correct number of + ** open savepoints. If the second parameter is greater than 0 and + ** the sub-journal is not already open, then it will be opened here. + */ + return sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint); + }else{ + return SQLITE_OK; + } +} #ifndef SQLITE_OMIT_AUTOVACUUM @@ -73538,7 +74357,6 @@ SQLITE_PRIVATE void sqlite3BtreeCursorUnpin(BtCursor *pCur){ pCur->curFlags &= ~BTCF_Pinned; } -#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC /* ** Return the offset into the database file for the start of the ** payload to which the cursor is pointing. @@ -73550,7 +74368,6 @@ SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor *pCur){ return (i64)pCur->pBt->pageSize*((i64)pCur->pPage->pgno - 1) + (i64)(pCur->info.pPayload - pCur->pPage->aData); } -#endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */ /* ** Return the number of bytes of payload for the entry that pCur is @@ -73576,7 +74393,7 @@ SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor *pCur){ ** routine always returns 2147483647 (which is the largest record ** that SQLite can handle) or more. But returning a smaller value might ** prevent large memory allocations when trying to interpret a -** corrupt datrabase. +** corrupt database. ** ** The current implementation merely returns the size of the underlying ** database file. @@ -74038,6 +74855,7 @@ SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt){ ** vice-versa). */ static int moveToChild(BtCursor *pCur, u32 newPgno){ + int rc; assert( cursorOwnsBtShared(pCur) ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPageapPage[pCur->iPage] = pCur->pPage; pCur->ix = 0; pCur->iPage++; - return getAndInitPage(pCur->pBt, newPgno, &pCur->pPage, pCur, - pCur->curPagerFlags); + rc = getAndInitPage(pCur->pBt, newPgno, &pCur->pPage, pCur->curPagerFlags); + assert( pCur->pPage!=0 || rc!=SQLITE_OK ); + if( rc==SQLITE_OK + && (pCur->pPage->nCell<1 || pCur->pPage->intKey!=pCur->curIntKey) + ){ + releasePage(pCur->pPage); + rc = SQLITE_CORRUPT_PGNO(newPgno); + } + if( rc ){ + pCur->pPage = pCur->apPage[--pCur->iPage]; + } + return rc; } #ifdef SQLITE_DEBUG @@ -74159,7 +74987,7 @@ static int moveToRoot(BtCursor *pCur){ sqlite3BtreeClearCursor(pCur); } rc = getAndInitPage(pCur->pBt, pCur->pgnoRoot, &pCur->pPage, - 0, pCur->curPagerFlags); + pCur->curPagerFlags); if( rc!=SQLITE_OK ){ pCur->eState = CURSOR_INVALID; return rc; @@ -74271,7 +75099,7 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ *pRes = 0; rc = moveToLeftmost(pCur); }else if( rc==SQLITE_EMPTY ){ - assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); + assert( pCur->pgnoRoot==0 || (pCur->pPage!=0 && pCur->pPage->nCell==0) ); *pRes = 1; rc = SQLITE_OK; } @@ -74376,7 +75204,7 @@ SQLITE_PRIVATE int sqlite3BtreeTableMoveto( /* If the requested key is one more than the previous key, then ** try to get there using sqlite3BtreeNext() rather than a full ** binary search. This is an optimization only. The correct answer - ** is still obtained without this case, only a little more slowely */ + ** is still obtained without this case, only a little more slowly. */ if( pCur->info.nKey+1==intKey ){ *pRes = 0; rc = sqlite3BtreeNext(pCur, 0); @@ -74772,10 +75600,36 @@ bypass_moveto_root: }else{ chldPg = get4byte(findCell(pPage, lwr)); } - pCur->ix = (u16)lwr; - rc = moveToChild(pCur, chldPg); - if( rc ) break; - } + + /* This block is similar to an in-lined version of: + ** + ** pCur->ix = (u16)lwr; + ** rc = moveToChild(pCur, chldPg); + ** if( rc ) break; + */ + pCur->info.nSize = 0; + pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); + if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){ + return SQLITE_CORRUPT_BKPT; + } + pCur->aiIdx[pCur->iPage] = (u16)lwr; + pCur->apPage[pCur->iPage] = pCur->pPage; + pCur->ix = 0; + pCur->iPage++; + rc = getAndInitPage(pCur->pBt, chldPg, &pCur->pPage, pCur->curPagerFlags); + if( rc==SQLITE_OK + && (pCur->pPage->nCell<1 || pCur->pPage->intKey!=pCur->curIntKey) + ){ + releasePage(pCur->pPage); + rc = SQLITE_CORRUPT_PGNO(chldPg); + } + if( rc ){ + pCur->pPage = pCur->apPage[--pCur->iPage]; + break; + } + /* + ***** End of in-lined moveToChild() call */ + } moveto_index_finish: pCur->info.nSize = 0; assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); @@ -75559,7 +76413,7 @@ static SQLITE_NOINLINE int clearCellOverflow( /* Call xParseCell to compute the size of a cell. If the cell contains ** overflow, then invoke cellClearOverflow to clear out that overflow. -** STore the result code (SQLITE_OK or some error code) in rc. +** Store the result code (SQLITE_OK or some error code) in rc. ** ** Implemented as macro to force inlining for performance. */ @@ -76175,7 +77029,7 @@ static int rebuildPage( if( NEVER(j>(u32)usableSize) ){ j = 0; } memcpy(&pTmp[j], &aData[j], usableSize - j); - for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kixNx[k]<=i; k++){} pSrcEnd = pCArray->apEnd[k]; pData = pEnd; @@ -76238,7 +77092,7 @@ static int rebuildPage( ** Finally, argument pBegin points to the byte immediately following the ** end of the space required by this page for the cell-pointer area (for ** all cells - not just those inserted by the current call). If the content -** area must be extended to before this point in order to accomodate all +** area must be extended to before this point in order to accommodate all ** cells in apCell[], then the cells do not fit and non-zero is returned. */ static int pageInsertArray( @@ -76258,7 +77112,7 @@ static int pageInsertArray( u8 *pEnd; /* Maximum extent of cell data */ assert( CORRUPT_DB || pPg->hdrOffset==0 ); /* Never called on page 1 */ if( iEnd<=iFirst ) return 0; - for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kixNx[k]<=i ; k++){} pEnd = pCArray->apEnd[k]; while( 1 /*Exit by break*/ ){ int sz, rc; @@ -76553,7 +77407,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ ** with entries for the new page, and any pointer from the ** cell on the page to an overflow page. If either of these ** operations fails, the return code is set, but the contents - ** of the parent page are still manipulated by thh code below. + ** of the parent page are still manipulated by the code below. ** That is Ok, at this point the parent page is guaranteed to ** be marked as dirty. Returning an error code will cause a ** rollback, undoing any changes made to the parent page. @@ -76829,7 +77683,7 @@ static int balance_nonroot( pgno = get4byte(pRight); while( 1 ){ if( rc==SQLITE_OK ){ - rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0); + rc = getAndInitPage(pBt, pgno, &apOld[i], 0); } if( rc ){ memset(apOld, 0, (i+1)*sizeof(MemPage*)); @@ -77143,7 +77997,7 @@ static int balance_nonroot( } } - /* Sanity check: For a non-corrupt database file one of the follwing + /* Sanity check: For a non-corrupt database file one of the following ** must be true: ** (1) We found one or more cells (cntNew[0])>0), or ** (2) pPage is a virtual root page. A virtual root page is when @@ -77368,9 +78222,9 @@ static int balance_nonroot( iOvflSpace += sz; assert( sz<=pBt->maxLocal+23 ); assert( iOvflSpace <= (int)pBt->pageSize ); - for(k=0; b.ixNx[k]<=j && ALWAYS(k=0 && iPg=1 || i>=0 ); + assert( iPg=0 /* On the upwards pass, or... */ || cntOld[iPg-1]>=cntNew[iPg-1] /* Condition (1) is true */ @@ -77760,7 +78616,7 @@ static int btreeOverwriteContent( ){ int nData = pX->nData - iOffset; if( nData<=0 ){ - /* Overwritting with zeros */ + /* Overwriting with zeros */ int i; for(i=0; ipData to write */ @@ -78543,7 +79399,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ MemPage *pRoot; Pgno pgnoRoot; int rc; - int ptfFlags; /* Page-type flage for the root page of new table */ + int ptfFlags; /* Page-type flags for the root page of new table */ assert( sqlite3BtreeHoldsMutex(p) ); assert( pBt->inTransaction==TRANS_WRITE ); @@ -78712,7 +79568,7 @@ static int clearDatabasePage( if( pgno>btreePagecount(pBt) ){ return SQLITE_CORRUPT_BKPT; } - rc = getAndInitPage(pBt, pgno, &pPage, 0, 0); + rc = getAndInitPage(pBt, pgno, &pPage, 0); if( rc ) return rc; if( (pBt->openFlags & BTREE_SINGLE)==0 && sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1)) @@ -79378,7 +80234,7 @@ static int checkTreePage( if( iPage==0 ) return 0; if( checkRef(pCheck, iPage) ) return 0; pCheck->zPfx = "Tree %u page %u: "; - pCheck->v0 = pCheck->v1 = iPage; + pCheck->v1 = iPage; if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){ checkAppendMsg(pCheck, "unable to get the page. error code=%d", rc); @@ -79715,6 +80571,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); } #endif + sCheck.v0 = aRoot[i]; checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); } pBt->db->flags = savedDbFlags; @@ -81141,6 +81998,40 @@ SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){ return SQLITE_OK; } +/* +** If pMem is already a string, detect if it is a zero-terminated +** string, or make it into one if possible, and mark it as such. +** +** This is an optimization. Correct operation continues even if +** this routine is a no-op. +*/ +SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){ + if( (pMem->flags & (MEM_Str|MEM_Term|MEM_Ephem|MEM_Static))!=MEM_Str ){ + /* pMem must be a string, and it cannot be an ephemeral or static string */ + return; + } + if( pMem->enc!=SQLITE_UTF8 ) return; + if( NEVER(pMem->z==0) ) return; + if( pMem->flags & MEM_Dyn ){ + if( pMem->xDel==sqlite3_free + && sqlite3_msize(pMem->z) >= (u64)(pMem->n+1) + ){ + pMem->z[pMem->n] = 0; + pMem->flags |= MEM_Term; + return; + } + if( pMem->xDel==(void(*)(void*))sqlite3RCStrUnref ){ + /* Blindly assume that all RCStr objects are zero-terminated */ + pMem->flags |= MEM_Term; + return; + } + }else if( pMem->szMalloc >= pMem->n+1 ){ + pMem->z[pMem->n] = 0; + pMem->flags |= MEM_Term; + return; + } +} + /* ** It is already known that pMem contains an unterminated string. ** Add the zero terminator. @@ -81402,36 +82293,6 @@ SQLITE_PRIVATE void sqlite3VdbeMemReleaseMalloc(Mem *p){ if( p->szMalloc ) vdbeMemClear(p); } -/* -** Convert a 64-bit IEEE double into a 64-bit signed integer. -** If the double is out of range of a 64-bit signed integer then -** return the closest available 64-bit signed integer. -*/ -static SQLITE_NOINLINE i64 doubleToInt64(double r){ -#ifdef SQLITE_OMIT_FLOATING_POINT - /* When floating-point is omitted, double and int64 are the same thing */ - return r; -#else - /* - ** Many compilers we encounter do not define constants for the - ** minimum and maximum 64-bit integers, or they define them - ** inconsistently. And many do not understand the "LL" notation. - ** So we define our own static constants here using nothing - ** larger than a 32-bit integer constant. - */ - static const i64 maxInt = LARGEST_INT64; - static const i64 minInt = SMALLEST_INT64; - - if( r<=(double)minInt ){ - return minInt; - }else if( r>=(double)maxInt ){ - return maxInt; - }else{ - return (i64)r; - } -#endif -} - /* ** Return some kind of integer value which is the best we can do ** at representing the value that *pMem describes as an integer. @@ -81458,7 +82319,7 @@ SQLITE_PRIVATE i64 sqlite3VdbeIntValue(const Mem *pMem){ testcase( flags & MEM_IntReal ); return pMem->u.i; }else if( flags & MEM_Real ){ - return doubleToInt64(pMem->u.r); + return sqlite3RealToI64(pMem->u.r); }else if( (flags & (MEM_Str|MEM_Blob))!=0 && pMem->z!=0 ){ return memIntValue(pMem); }else{ @@ -81520,7 +82381,7 @@ SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){ if( pMem->flags & MEM_IntReal ){ MemSetTypeFlag(pMem, MEM_Int); }else{ - i64 ix = doubleToInt64(pMem->u.r); + i64 ix = sqlite3RealToI64(pMem->u.r); /* Only mark the value as an integer if ** @@ -81588,8 +82449,8 @@ SQLITE_PRIVATE int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){ ** from UBSAN. */ SQLITE_PRIVATE i64 sqlite3RealToI64(double r){ - if( r<=(double)SMALLEST_INT64 ) return SMALLEST_INT64; - if( r>=(double)LARGEST_INT64) return LARGEST_INT64; + if( r<-9223372036854774784.0 ) return SMALLEST_INT64; + if( r>+9223372036854774784.0 ) return LARGEST_INT64; return (i64)r; } @@ -81660,6 +82521,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){ break; } default: { + int rc; assert( aff==SQLITE_AFF_TEXT ); assert( MEM_Str==(MEM_Blob>>3) ); pMem->flags |= (pMem->flags&MEM_Blob)>>3; @@ -81667,7 +82529,9 @@ SQLITE_PRIVATE int sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){ assert( pMem->flags & MEM_Str || pMem->db->mallocFailed ); pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal|MEM_Blob|MEM_Zero); if( encoding!=SQLITE_UTF8 ) pMem->n &= ~1; - return sqlite3VdbeChangeEncoding(pMem, encoding); + rc = sqlite3VdbeChangeEncoding(pMem, encoding); + if( rc ) return rc; + sqlite3VdbeMemZeroTerminateIfAble(pMem); } } return SQLITE_OK; @@ -82191,6 +83055,24 @@ SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){ return valueToText(pVal, enc); } +/* Return true if sqlit3_value object pVal is a string or blob value +** that uses the destructor specified in the second argument. +** +** TODO: Maybe someday promote this interface into a published API so +** that third-party extensions can get access to it? +*/ +SQLITE_PRIVATE int sqlite3ValueIsOfClass(const sqlite3_value *pVal, void(*xFree)(void*)){ + if( ALWAYS(pVal!=0) + && ALWAYS((pVal->flags & (MEM_Str|MEM_Blob))!=0) + && (pVal->flags & MEM_Dyn)!=0 + && pVal->xDel==xFree + ){ + return 1; + }else{ + return 0; + } +} + /* ** Create a new sqlite3_value object. */ @@ -82258,6 +83140,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ } pRec->nField = p->iVal+1; + sqlite3VdbeMemSetNull(&pRec->aMem[p->iVal]); return &pRec->aMem[p->iVal]; } #else @@ -83046,6 +83929,35 @@ static void test_addop_breakpoint(int pc, Op *pOp){ } #endif +/* +** Slow paths for sqlite3VdbeAddOp3() and sqlite3VdbeAddOp4Int() for the +** unusual case when we need to increase the size of the Vdbe.aOp[] array +** before adding the new opcode. +*/ +static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){ + assert( p->nOpAlloc<=p->nOp ); + if( growOpArray(p, 1) ) return 1; + assert( p->nOpAlloc>p->nOp ); + return sqlite3VdbeAddOp3(p, op, p1, p2, p3); +} +static SQLITE_NOINLINE int addOp4IntSlow( + Vdbe *p, /* Add the opcode to this VM */ + int op, /* The new opcode */ + int p1, /* The P1 operand */ + int p2, /* The P2 operand */ + int p3, /* The P3 operand */ + int p4 /* The P4 operand as an integer */ +){ + int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3); + if( p->db->mallocFailed==0 ){ + VdbeOp *pOp = &p->aOp[addr]; + pOp->p4type = P4_INT32; + pOp->p4.i = p4; + } + return addr; +} + + /* ** Add a new instruction to the list of instructions current in the ** VDBE. Return the address of the new instruction. @@ -83056,17 +83968,16 @@ static void test_addop_breakpoint(int pc, Op *pOp){ ** ** op The opcode for this instruction ** -** p1, p2, p3 Operands -** -** Use the sqlite3VdbeResolveLabel() function to fix an address and -** the sqlite3VdbeChangeP4() function to change the value of the P4 -** operand. +** p1, p2, p3, p4 Operands */ -static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){ - assert( p->nOpAlloc<=p->nOp ); - if( growOpArray(p, 1) ) return 1; - assert( p->nOpAlloc>p->nOp ); - return sqlite3VdbeAddOp3(p, op, p1, p2, p3); +SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe *p, int op){ + return sqlite3VdbeAddOp3(p, op, 0, 0, 0); +} +SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe *p, int op, int p1){ + return sqlite3VdbeAddOp3(p, op, p1, 0, 0); +} +SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){ + return sqlite3VdbeAddOp3(p, op, p1, p2, 0); } SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ int i; @@ -83089,6 +84000,9 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ pOp->p3 = p3; pOp->p4.p = 0; pOp->p4type = P4_NOTUSED; + + /* Replicate this logic in sqlite3VdbeAddOp4Int() + ** vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS pOp->zComment = 0; #endif @@ -83105,16 +84019,59 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ #ifdef SQLITE_VDBE_COVERAGE pOp->iSrcLine = 0; #endif + /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ** Replicate in sqlite3VdbeAddOp4Int() */ + return i; } -SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe *p, int op){ - return sqlite3VdbeAddOp3(p, op, 0, 0, 0); -} -SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe *p, int op, int p1){ - return sqlite3VdbeAddOp3(p, op, p1, 0, 0); -} -SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){ - return sqlite3VdbeAddOp3(p, op, p1, p2, 0); +SQLITE_PRIVATE int sqlite3VdbeAddOp4Int( + Vdbe *p, /* Add the opcode to this VM */ + int op, /* The new opcode */ + int p1, /* The P1 operand */ + int p2, /* The P2 operand */ + int p3, /* The P3 operand */ + int p4 /* The P4 operand as an integer */ +){ + int i; + VdbeOp *pOp; + + i = p->nOp; + if( p->nOpAlloc<=i ){ + return addOp4IntSlow(p, op, p1, p2, p3, p4); + } + p->nOp++; + pOp = &p->aOp[i]; + assert( pOp!=0 ); + pOp->opcode = (u8)op; + pOp->p5 = 0; + pOp->p1 = p1; + pOp->p2 = p2; + pOp->p3 = p3; + pOp->p4.i = p4; + pOp->p4type = P4_INT32; + + /* Replicate this logic in sqlite3VdbeAddOp3() + ** vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + pOp->zComment = 0; +#endif +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) + pOp->nExec = 0; + pOp->nCycle = 0; +#endif +#ifdef SQLITE_DEBUG + if( p->db->flags & SQLITE_VdbeAddopTrace ){ + sqlite3VdbePrintOp(0, i, &p->aOp[i]); + test_addop_breakpoint(i, &p->aOp[i]); + } +#endif +#ifdef SQLITE_VDBE_COVERAGE + pOp->iSrcLine = 0; +#endif + /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ** Replicate in sqlite3VdbeAddOp3() */ + + return i; } /* Generate code for an unconditional jump to instruction iDest @@ -83292,7 +84249,7 @@ SQLITE_PRIVATE int sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, if( bPush){ pParse->addrExplain = iThis; } - sqlite3VdbeScanStatus(v, iThis, 0, 0, 0, 0); + sqlite3VdbeScanStatus(v, iThis, -1, -1, 0, 0); } return addr; } @@ -83322,26 +84279,6 @@ SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere, sqlite3MayAbort(p->pParse); } -/* -** Add an opcode that includes the p4 value as an integer. -*/ -SQLITE_PRIVATE int sqlite3VdbeAddOp4Int( - Vdbe *p, /* Add the opcode to this VM */ - int op, /* The new opcode */ - int p1, /* The P1 operand */ - int p2, /* The P2 operand */ - int p3, /* The P3 operand */ - int p4 /* The P4 operand as an integer */ -){ - int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3); - if( p->db->mallocFailed==0 ){ - VdbeOp *pOp = &p->aOp[addr]; - pOp->p4type = P4_INT32; - pOp->p4.i = p4; - } - return addr; -} - /* Insert the end of a co-routine */ SQLITE_PRIVATE void sqlite3VdbeEndCoroutine(Vdbe *v, int regYield){ @@ -83654,7 +84591,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ p->bIsReader = 0; pOp = &p->aOp[p->nOp-1]; assert( p->aOp[0].opcode==OP_Init ); - while( 1 /* Loop termates when it reaches the OP_Init opcode */ ){ + while( 1 /* Loop terminates when it reaches the OP_Init opcode */ ){ /* Only JUMP opcodes and the short list of special opcodes in the switch ** below need to be considered. The mkopcodeh.tcl generator script groups ** all these opcodes together near the front of the opcode list. Skip @@ -84024,8 +84961,8 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters( pScan = 0; } if( pScan ){ - pScan->addrLoop = addrLoop; - pScan->addrVisit = addrVisit; + if( addrLoop>0 ) pScan->addrLoop = addrLoop; + if( addrVisit>0 ) pScan->addrVisit = addrVisit; } } } @@ -84108,7 +85045,7 @@ SQLITE_PRIVATE void sqlite3VdbeJumpHereOrPopInst(Vdbe *p, int addr){ /* ** If the input FuncDef structure is ephemeral, then free it. If -** the FuncDef is not ephermal, then do nothing. +** the FuncDef is not ephemeral, then do nothing. */ static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){ assert( db!=0 ); @@ -84272,7 +85209,6 @@ SQLITE_PRIVATE void sqlite3VdbeReleaseRegisters( } #endif /* SQLITE_DEBUG */ - /* ** Change the value of the P4 operand for a specific instruction. ** This routine is useful when a large program is loaded from a @@ -85193,7 +86129,7 @@ SQLITE_PRIVATE int sqlite3VdbeList( sqlite3VdbeMemSetInt64(pMem+1, pOp->p2); sqlite3VdbeMemSetInt64(pMem+2, pOp->p3); sqlite3VdbeMemSetStr(pMem+3, zP4, -1, SQLITE_UTF8, sqlite3_free); - p->nResColumn = 4; + assert( p->nResColumn==4 ); }else{ sqlite3VdbeMemSetInt64(pMem+0, i); sqlite3VdbeMemSetStr(pMem+1, (char*)sqlite3OpcodeName(pOp->opcode), @@ -85212,7 +86148,7 @@ SQLITE_PRIVATE int sqlite3VdbeList( sqlite3VdbeMemSetNull(pMem+7); #endif sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlite3_free); - p->nResColumn = 8; + assert( p->nResColumn==8 ); } p->pResultRow = pMem; if( db->mallocFailed ){ @@ -85426,26 +86362,9 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( resolveP2Values(p, &nArg); p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort); if( pParse->explain ){ - static const char * const azColName[] = { - "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", - "id", "parent", "notused", "detail" - }; - int iFirst, mx, i; if( nMem<10 ) nMem = 10; p->explain = pParse->explain; - if( pParse->explain==2 ){ - sqlite3VdbeSetNumCols(p, 4); - iFirst = 8; - mx = 12; - }else{ - sqlite3VdbeSetNumCols(p, 8); - iFirst = 0; - mx = 8; - } - for(i=iFirst; inResColumn = 12 - 4*p->explain; } p->expired = 0; @@ -85497,7 +86416,23 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ if( pCx ) sqlite3VdbeFreeCursorNN(p,pCx); } +static SQLITE_NOINLINE void freeCursorWithCache(Vdbe *p, VdbeCursor *pCx){ + VdbeTxtBlbCache *pCache = pCx->pCache; + assert( pCx->colCache ); + pCx->colCache = 0; + pCx->pCache = 0; + if( pCache->pCValue ){ + sqlite3RCStrUnref(pCache->pCValue); + pCache->pCValue = 0; + } + sqlite3DbFree(p->db, pCache); + sqlite3VdbeFreeCursorNN(p, pCx); +} SQLITE_PRIVATE void sqlite3VdbeFreeCursorNN(Vdbe *p, VdbeCursor *pCx){ + if( pCx->colCache ){ + freeCursorWithCache(p, pCx); + return; + } switch( pCx->eCurType ){ case CURTYPE_SORTER: { sqlite3VdbeSorterClose(p->db, pCx); @@ -85598,12 +86533,12 @@ SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){ int n; sqlite3 *db = p->db; - if( p->nResColumn ){ - releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); + if( p->nResAlloc ){ + releaseMemArray(p->aColName, p->nResAlloc*COLNAME_N); sqlite3DbFree(db, p->aColName); } n = nResColumn*COLNAME_N; - p->nResColumn = (u16)nResColumn; + p->nResColumn = p->nResAlloc = (u16)nResColumn; p->aColName = (Mem*)sqlite3DbMallocRawNN(db, sizeof(Mem)*n ); if( p->aColName==0 ) return; initMemArray(p->aColName, n, db, MEM_Null); @@ -85628,14 +86563,14 @@ SQLITE_PRIVATE int sqlite3VdbeSetColName( ){ int rc; Mem *pColName; - assert( idxnResColumn ); + assert( idxnResAlloc ); assert( vardb->mallocFailed ){ assert( !zName || xDel!=SQLITE_DYNAMIC ); return SQLITE_NOMEM_BKPT; } assert( p->aColName!=0 ); - pColName = &(p->aColName[idx+var*p->nResColumn]); + pColName = &(p->aColName[idx+var*p->nResAlloc]); rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, xDel); assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 ); return rc; @@ -86148,6 +87083,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ sqlite3VdbeLeave(p); return SQLITE_BUSY; }else if( rc!=SQLITE_OK ){ + sqlite3SystemError(db, rc); p->rc = rc; sqlite3RollbackAll(db, SQLITE_OK); p->nChange = 0; @@ -86459,7 +87395,7 @@ static void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ assert( db!=0 ); assert( p->db==0 || p->db==db ); if( p->aColName ){ - releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); + releaseMemArray(p->aColName, p->nResAlloc*COLNAME_N); sqlite3DbNNFreeNN(db, p->aColName); } for(pSub=p->pProgram; pSub; pSub=pNext){ @@ -87059,6 +87995,15 @@ static int vdbeRecordCompareDebug( if( d1+(u64)serial_type1+2>(u64)nKey1 && d1+(u64)sqlite3VdbeSerialTypeLen(serial_type1)>(u64)nKey1 ){ + if( serial_type1>=1 + && serial_type1<=7 + && d1+(u64)sqlite3VdbeSerialTypeLen(serial_type1)<=(u64)nKey1+8 + && CORRUPT_DB + ){ + return 1; /* corrupt record not detected by + ** sqlite3VdbeRecordCompareWithSkip(). Return true + ** to avoid firing the assert() */ + } break; } @@ -87502,7 +88447,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( /* Serial types 12 or greater are strings and blobs (greater than ** numbers). Types 10 and 11 are currently "reserved for future ** use", so it doesn't really matter what the results of comparing - ** them to numberic values are. */ + ** them to numeric values are. */ rc = serial_type==10 ? -1 : +1; }else if( serial_type==0 ){ rc = -1; @@ -88759,6 +89704,7 @@ SQLITE_API void sqlite3_result_text64( (void)invokeValueDestructor(z, xDel, pCtx); }else{ setResultStrOrError(pCtx, z, (int)n, enc, xDel); + sqlite3VdbeMemZeroTerminateIfAble(pCtx->pOut); } } #ifndef SQLITE_OMIT_UTF16 @@ -89131,7 +90077,7 @@ SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){ ** The destructor function for a ValueList object. This needs to be ** a separate function, unknowable to the application, to ensure that ** calls to sqlite3_vtab_in_first()/sqlite3_vtab_in_next() that are not -** preceeded by activation of IN processing via sqlite3_vtab_int() do not +** preceded by activation of IN processing via sqlite3_vtab_int() do not ** try to access a fake ValueList object inserted by a hostile extension. */ SQLITE_PRIVATE void sqlite3VdbeValueListFree(void *pToDelete){ @@ -89371,7 +90317,8 @@ SQLITE_API int sqlite3_aggregate_count(sqlite3_context *p){ */ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt){ Vdbe *pVm = (Vdbe *)pStmt; - return pVm ? pVm->nResColumn : 0; + if( pVm==0 ) return 0; + return pVm->nResColumn; } /* @@ -89460,7 +90407,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ ** sqlite3_column_real() ** sqlite3_column_bytes() ** sqlite3_column_bytes16() -** sqiite3_column_blob() +** sqlite3_column_blob() */ static void columnMallocFailure(sqlite3_stmt *pStmt) { @@ -89544,6 +90491,32 @@ SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){ return iType; } +/* +** Column names appropriate for EXPLAIN or EXPLAIN QUERY PLAN. +*/ +static const char * const azExplainColNames8[] = { + "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", /* EXPLAIN */ + "id", "parent", "notused", "detail" /* EQP */ +}; +static const u16 azExplainColNames16data[] = { + /* 0 */ 'a', 'd', 'd', 'r', 0, + /* 5 */ 'o', 'p', 'c', 'o', 'd', 'e', 0, + /* 12 */ 'p', '1', 0, + /* 15 */ 'p', '2', 0, + /* 18 */ 'p', '3', 0, + /* 21 */ 'p', '4', 0, + /* 24 */ 'p', '5', 0, + /* 27 */ 'c', 'o', 'm', 'm', 'e', 'n', 't', 0, + /* 35 */ 'i', 'd', 0, + /* 38 */ 'p', 'a', 'r', 'e', 'n', 't', 0, + /* 45 */ 'n', 'o', 't', 'u', 's', 'e', 'd', 0, + /* 53 */ 'd', 'e', 't', 'a', 'i', 'l', 0 +}; +static const u8 iExplainColNames16[] = { + 0, 5, 12, 15, 18, 21, 24, 27, + 35, 38, 45, 53 +}; + /* ** Convert the N-th element of pStmt->pColName[] into a string using ** xFunc() then return that string. If N is out of range, return 0. @@ -89576,15 +90549,29 @@ static const void *columnName( return 0; } #endif + if( N<0 ) return 0; ret = 0; p = (Vdbe *)pStmt; db = p->db; assert( db!=0 ); - n = sqlite3_column_count(pStmt); - if( N=0 ){ + sqlite3_mutex_enter(db->mutex); + + if( p->explain ){ + if( useType>0 ) goto columnName_end; + n = p->explain==1 ? 8 : 4; + if( N>=n ) goto columnName_end; + if( useUtf16 ){ + int i = iExplainColNames16[N + 8*p->explain - 8]; + ret = (void*)&azExplainColNames16data[i]; + }else{ + ret = (void*)azExplainColNames8[N + 8*p->explain - 8]; + } + goto columnName_end; + } + n = p->nResColumn; + if( NmallocFailed; N += useType*n; - sqlite3_mutex_enter(db->mutex); #ifndef SQLITE_OMIT_UTF16 if( useUtf16 ){ ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]); @@ -89601,8 +90588,9 @@ static const void *columnName( sqlite3OomClear(db); ret = 0; } - sqlite3_mutex_leave(db->mutex); } +columnName_end: + sqlite3_mutex_leave(db->mutex); return ret; } @@ -89695,7 +90683,7 @@ SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){ /* ** Unbind the value bound to variable i in virtual machine p. This is the ** the same as binding a NULL value to the column. If the "i" parameter is -** out of range, then SQLITE_RANGE is returned. Othewise SQLITE_OK. +** out of range, then SQLITE_RANGE is returned. Otherwise SQLITE_OK. ** ** A successful evaluation of this routine acquires the mutex on p. ** the mutex is released if any kind of error occurs. @@ -90059,6 +91047,39 @@ SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->explain : 0; } +/* +** Set the explain mode for a statement. +*/ +SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode){ + Vdbe *v = (Vdbe*)pStmt; + int rc; + sqlite3_mutex_enter(v->db->mutex); + if( ((int)v->explain)==eMode ){ + rc = SQLITE_OK; + }else if( eMode<0 || eMode>2 ){ + rc = SQLITE_ERROR; + }else if( (v->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ){ + rc = SQLITE_ERROR; + }else if( v->eVdbeState!=VDBE_READY_STATE ){ + rc = SQLITE_BUSY; + }else if( v->nMem>=10 && (eMode!=2 || v->haveEqpOps) ){ + /* No reprepare necessary */ + v->explain = eMode; + rc = SQLITE_OK; + }else{ + v->explain = eMode; + rc = sqlite3Reprepare(v); + v->haveEqpOps = eMode==2; + } + if( v->explain ){ + v->nResColumn = 12 - 4*v->explain; + }else{ + v->nResColumn = v->nResAlloc; + } + sqlite3_mutex_leave(v->db->mutex); + return rc; +} + /* ** Return true if the prepared statement is in need of being reset. */ @@ -91298,6 +92319,9 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr){ sqlite3_str_appendchar(pStr, 1, (c>=0x20&&c<=0x7f) ? c : '.'); } sqlite3_str_appendf(pStr, "]%s", encnames[pMem->enc]); + if( f & MEM_Term ){ + sqlite3_str_appendf(pStr, "(0-term)"); + } } } #endif @@ -91434,6 +92458,93 @@ static u64 filterHash(const Mem *aMem, const Op *pOp){ return h; } + +/* +** For OP_Column, factor out the case where content is loaded from +** overflow pages, so that the code to implement this case is separate +** the common case where all content fits on the page. Factoring out +** the code reduces register pressure and helps the common case +** to run faster. +*/ +static SQLITE_NOINLINE int vdbeColumnFromOverflow( + VdbeCursor *pC, /* The BTree cursor from which we are reading */ + int iCol, /* The column to read */ + int t, /* The serial-type code for the column value */ + i64 iOffset, /* Offset to the start of the content value */ + u32 cacheStatus, /* Current Vdbe.cacheCtr value */ + u32 colCacheCtr, /* Current value of the column cache counter */ + Mem *pDest /* Store the value into this register. */ +){ + int rc; + sqlite3 *db = pDest->db; + int encoding = pDest->enc; + int len = sqlite3VdbeSerialTypeLen(t); + assert( pC->eCurType==CURTYPE_BTREE ); + if( len>db->aLimit[SQLITE_LIMIT_LENGTH] ) return SQLITE_TOOBIG; + if( len > 4000 && pC->pKeyInfo==0 ){ + /* Cache large column values that are on overflow pages using + ** an RCStr (reference counted string) so that if they are reloaded, + ** that do not have to be copied a second time. The overhead of + ** creating and managing the cache is such that this is only + ** profitable for larger TEXT and BLOB values. + ** + ** Only do this on table-btrees so that writes to index-btrees do not + ** need to clear the cache. This buys performance in the common case + ** in exchange for generality. + */ + VdbeTxtBlbCache *pCache; + char *pBuf; + if( pC->colCache==0 ){ + pC->pCache = sqlite3DbMallocZero(db, sizeof(VdbeTxtBlbCache) ); + if( pC->pCache==0 ) return SQLITE_NOMEM; + pC->colCache = 1; + } + pCache = pC->pCache; + if( pCache->pCValue==0 + || pCache->iCol!=iCol + || pCache->cacheStatus!=cacheStatus + || pCache->colCacheCtr!=colCacheCtr + || pCache->iOffset!=sqlite3BtreeOffset(pC->uc.pCursor) + ){ + if( pCache->pCValue ) sqlite3RCStrUnref(pCache->pCValue); + pBuf = pCache->pCValue = sqlite3RCStrNew( len+3 ); + if( pBuf==0 ) return SQLITE_NOMEM; + rc = sqlite3BtreePayload(pC->uc.pCursor, iOffset, len, pBuf); + if( rc ) return rc; + pBuf[len] = 0; + pBuf[len+1] = 0; + pBuf[len+2] = 0; + pCache->iCol = iCol; + pCache->cacheStatus = cacheStatus; + pCache->colCacheCtr = colCacheCtr; + pCache->iOffset = sqlite3BtreeOffset(pC->uc.pCursor); + }else{ + pBuf = pCache->pCValue; + } + assert( t>=12 ); + sqlite3RCStrRef(pBuf); + if( t&1 ){ + rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, encoding, + (void(*)(void*))sqlite3RCStrUnref); + pDest->flags |= MEM_Term; + }else{ + rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, 0, + (void(*)(void*))sqlite3RCStrUnref); + } + }else{ + rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, iOffset, len, pDest); + if( rc ) return rc; + sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); + if( (t&1)!=0 && encoding==SQLITE_UTF8 ){ + pDest->z[len] = 0; + pDest->flags |= MEM_Term; + } + } + pDest->flags &= ~MEM_Ephem; + return rc; +} + + /* ** Return the symbolic name for the data type of a pMem */ @@ -91476,6 +92587,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec( Mem *pIn2 = 0; /* 2nd input operand */ Mem *pIn3 = 0; /* 3rd input operand */ Mem *pOut = 0; /* Output operand */ + u32 colCacheCtr = 0; /* Column cache counter */ #if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) u64 *pnCycle = 0; int bStmtScanStatus = IS_STMT_SCANSTATUS(db)!=0; @@ -91671,8 +92783,8 @@ SQLITE_PRIVATE int sqlite3VdbeExec( case OP_Goto: { /* jump */ #ifdef SQLITE_DEBUG - /* In debuggging mode, when the p5 flags is set on an OP_Goto, that - ** means we should really jump back to the preceeding OP_ReleaseReg + /* In debugging mode, when the p5 flags is set on an OP_Goto, that + ** means we should really jump back to the preceding OP_ReleaseReg ** instruction. */ if( pOp->p5 ){ assert( pOp->p2 < (int)(pOp - aOp) ); @@ -91880,7 +92992,7 @@ case OP_HaltIfNull: { /* in3 */ ** P5 is a value between 0 and 4, inclusive, that modifies the P4 string. ** ** 0: (no change) -** 1: NOT NULL contraint failed: P4 +** 1: NOT NULL constraint failed: P4 ** 2: UNIQUE constraint failed: P4 ** 3: CHECK constraint failed: P4 ** 4: FOREIGN KEY constraint failed: P4 @@ -93011,10 +94123,10 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ ** opcodes are allowed to occur between this instruction and the previous ** OP_Lt or OP_Gt. ** -** If result of an OP_Eq comparison on the same two operands as the -** prior OP_Lt or OP_Gt would have been true, then jump to P2. -** If the result of an OP_Eq comparison on the two previous -** operands would have been false or NULL, then fall through. +** If the result of an OP_Eq comparison on the same two operands as +** the prior OP_Lt or OP_Gt would have been true, then jump to P2. If +** the result of an OP_Eq comparison on the two previous operands +** would have been false or NULL, then fall through. */ case OP_ElseEq: { /* same as TK_ESCAPE, jump */ @@ -93444,7 +94556,7 @@ case OP_IsType: { /* jump */ /* Opcode: ZeroOrNull P1 P2 P3 * * ** Synopsis: r[P2] = 0 OR NULL ** -** If all both registers P1 and P3 are NOT NULL, then store a zero in +** If both registers P1 and P3 are NOT NULL, then store a zero in ** register P2. If either registers P1 or P3 are NULL then put ** a NULL in register P2. */ @@ -93798,11 +94910,16 @@ op_column_restart: pDest->flags = aFlag[t&1]; } }else{ + u8 p5; pDest->enc = encoding; + assert( pDest->db==db ); /* This branch happens only when content is on overflow pages */ - if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 - && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) - || (len = sqlite3VdbeSerialTypeLen(t))==0 + if( ((p5 = (pOp->p5 & OPFLAG_BYTELENARG))!=0 + && (p5==OPFLAG_TYPEOFARG + || (t>=12 && ((t&1)==0 || p5==OPFLAG_BYTELENARG)) + ) + ) + || sqlite3VdbeSerialTypeLen(t)==0 ){ /* Content is irrelevant for ** 1. the typeof() function, @@ -93819,11 +94936,13 @@ op_column_restart: */ sqlite3VdbeSerialGet((u8*)sqlite3CtypeMap, t, pDest); }else{ - if( len>db->aLimit[SQLITE_LIMIT_LENGTH] ) goto too_big; - rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, aOffset[p2], len, pDest); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); - pDest->flags &= ~MEM_Ephem; + rc = vdbeColumnFromOverflow(pC, p2, t, aOffset[p2], + p->cacheCtr, colCacheCtr, pDest); + if( rc ){ + if( rc==SQLITE_NOMEM ) goto no_mem; + if( rc==SQLITE_TOOBIG ) goto too_big; + goto abort_due_to_error; + } } } @@ -95107,7 +96226,7 @@ case OP_OpenEphemeral: { /* ncycle */ } pCx = p->apCsr[pOp->p1]; if( pCx && !pCx->noReuse && ALWAYS(pOp->p2<=pCx->nField) ){ - /* If the ephermeral table is already open and has no duplicates from + /* If the ephemeral table is already open and has no duplicates from ** OP_OpenDup, then erase all existing content so that the table is ** empty again, rather than creating a new table. */ assert( pCx->isEphemeral ); @@ -95598,7 +96717,7 @@ seek_not_found: ** row. If This.P5 is false (0) then a jump is made to SeekGE.P2. If ** This.P5 is true (non-zero) then a jump is made to This.P2. The P5==0 ** case occurs when there are no inequality constraints to the right of -** the IN constraing. The jump to SeekGE.P2 ends the loop. The P5!=0 case +** the IN constraint. The jump to SeekGE.P2 ends the loop. The P5!=0 case ** occurs when there are inequality constraints to the right of the IN ** operator. In that case, the This.P2 will point either directly to or ** to setup code prior to the OP_IdxGT or OP_IdxGE opcode that checks for @@ -95606,7 +96725,7 @@ seek_not_found: ** ** Possible outcomes from this opcode:
      ** -**
    1. If the cursor is initally not pointed to any valid row, then +**
    2. If the cursor is initially not pointed to any valid row, then ** fall through into the subsequent OP_SeekGE opcode. ** **
    3. If the cursor is left pointing to a row that is before the target @@ -95838,13 +96957,13 @@ case OP_IfNotOpen: { /* jump */ ** operands to OP_NotFound and OP_IdxGT. ** ** This opcode is an optimization attempt only. If this opcode always -** falls through, the correct answer is still obtained, but extra works +** falls through, the correct answer is still obtained, but extra work ** is performed. ** ** A value of N in the seekHit flag of cursor P1 means that there exists ** a key P3:N that will match some record in the index. We want to know ** if it is possible for a record P3:P4 to match some record in the -** index. If it is not possible, we can skips some work. So if seekHit +** index. If it is not possible, we can skip some work. So if seekHit ** is less than P4, attempt to find out if a match is possible by running ** OP_NotFound. ** @@ -96356,6 +97475,7 @@ case OP_Insert: { ); pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; + colCacheCtr++; /* Invoke the update-hook if required. */ if( rc ) goto abort_due_to_error; @@ -96409,10 +97529,10 @@ case OP_RowCell: { ** left in an undefined state. ** ** If the OPFLAG_AUXDELETE bit is set on P5, that indicates that this -** delete one of several associated with deleting a table row and all its -** associated index entries. Exactly one of those deletes is the "primary" -** delete. The others are all on OPFLAG_FORDELETE cursors or else are -** marked with the AUXDELETE flag. +** delete is one of several associated with deleting a table row and +** all its associated index entries. Exactly one of those deletes is +** the "primary" delete. The others are all on OPFLAG_FORDELETE +** cursors or else are marked with the AUXDELETE flag. ** ** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row ** change count is incremented (otherwise not). @@ -96516,6 +97636,7 @@ case OP_Delete: { rc = sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5); pC->cacheStatus = CACHE_STALE; + colCacheCtr++; pC->seekResult = 0; if( rc ) goto abort_due_to_error; @@ -96583,13 +97704,13 @@ case OP_SorterCompare: { ** Write into register P2 the current sorter data for sorter cursor P1. ** Then clear the column header cache on cursor P3. ** -** This opcode is normally use to move a record out of the sorter and into +** This opcode is normally used to move a record out of the sorter and into ** a register that is the source for a pseudo-table cursor created using ** OpenPseudo. That pseudo-table cursor is the one that is identified by ** parameter P3. Clearing the P3 column cache as part of this opcode saves ** us from having to issue a separate NullRow instruction to clear that cache. */ -case OP_SorterData: { +case OP_SorterData: { /* ncycle */ VdbeCursor *pC; pOut = &aMem[pOp->p2]; @@ -96864,8 +97985,8 @@ case OP_IfSmaller: { /* jump */ ** regression tests can determine whether or not the optimizer is ** correctly optimizing out sorts. */ -case OP_SorterSort: /* jump */ -case OP_Sort: { /* jump */ +case OP_SorterSort: /* jump ncycle */ +case OP_Sort: { /* jump ncycle */ #ifdef SQLITE_TEST sqlite3_sort_count++; sqlite3_search_count--; @@ -97392,7 +98513,7 @@ case OP_IdxGE: { /* jump, ncycle */ ** file is given by P1. ** ** The table being destroyed is in the main database file if P3==0. If -** P3==1 then the table to be clear is in the auxiliary database file +** P3==1 then the table to be destroyed is in the auxiliary database file ** that is used to store tables create using CREATE TEMPORARY TABLE. ** ** If AUTOVACUUM is enabled then it is possible that another root page @@ -97452,8 +98573,8 @@ case OP_Destroy: { /* out2 */ ** in the database file is given by P1. But, unlike Destroy, do not ** remove the table or index from the database file. ** -** The table being clear is in the main database file if P2==0. If -** P2==1 then the table to be clear is in the auxiliary database file +** The table being cleared is in the main database file if P2==0. If +** P2==1 then the table to be cleared is in the auxiliary database file ** that is used to store tables create using CREATE TEMPORARY TABLE. ** ** If the P3 value is non-zero, then the row change count is incremented @@ -98279,7 +99400,7 @@ case OP_AggStep1: { /* If this function is inside of a trigger, the register array in aMem[] ** might change from one evaluation to the next. The next block of code ** checks to see if the register array has changed, and if so it - ** reinitializes the relavant parts of the sqlite3_context object */ + ** reinitializes the relevant parts of the sqlite3_context object */ if( pCtx->pMem != pMem ){ pCtx->pMem = pMem; for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i]; @@ -99157,7 +100278,7 @@ case OP_MaxPgcnt: { /* out2 */ ** This opcode works exactly like OP_Function. The only difference is in ** its name. This opcode is used in places where the function must be ** purely non-deterministic. Some built-in date/time functions can be -** either determinitic of non-deterministic, depending on their arguments. +** either deterministic of non-deterministic, depending on their arguments. ** When those function are used in a non-deterministic way, they will check ** to see if they were called using OP_PureFunc instead of OP_Function, and ** if they were, they throw an error. @@ -99175,7 +100296,7 @@ case OP_Function: { /* group */ /* If this function is inside of a trigger, the register array in aMem[] ** might change from one evaluation to the next. The next block of code ** checks to see if the register array has changed, and if so it - ** reinitializes the relavant parts of the sqlite3_context object */ + ** reinitializes the relevant parts of the sqlite3_context object */ pOut = &aMem[pOp->p3]; if( pCtx->pOut != pOut ){ pCtx->pVdbe = p; @@ -99251,7 +100372,7 @@ case OP_FilterAdd: { printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n)); } #endif - h %= pIn1->n; + h %= (pIn1->n*8); pIn1->z[h/8] |= 1<<(h&7); break; } @@ -99287,7 +100408,7 @@ case OP_Filter: { /* jump */ printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n)); } #endif - h %= pIn1->n; + h %= (pIn1->n*8); if( (pIn1->z[h/8] & (1<<(h&7)))==0 ){ VdbeBranchTaken(1, 2); p->aCounter[SQLITE_STMTSTATUS_FILTER_HIT]++; @@ -99539,7 +100660,7 @@ default: { /* This is really OP_Noop, OP_Explain */ } if( opProperty==0xff ){ /* Never happens. This code exists to avoid a harmless linkage - ** warning aboud sqlite3VdbeRegisterDump() being defined but not + ** warning about sqlite3VdbeRegisterDump() being defined but not ** used. */ sqlite3VdbeRegisterDump(p); } @@ -100257,7 +101378,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ ** The threshold for the amount of main memory to use before flushing ** records to a PMA is roughly the same as the limit configured for the ** page-cache of the main database. Specifically, the threshold is set to -** the value returned by "PRAGMA main.page_size" multipled by +** the value returned by "PRAGMA main.page_size" multiplied by ** that returned by "PRAGMA main.cache_size", in bytes. ** ** If the sorter is running in single-threaded mode, then all PMAs generated @@ -100280,7 +101401,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ ** ** If there are fewer than SORTER_MAX_MERGE_COUNT PMAs in total and the ** sorter is running in single-threaded mode, then these PMAs are merged -** incrementally as keys are retreived from the sorter by the VDBE. The +** incrementally as keys are retrieved from the sorter by the VDBE. The ** MergeEngine object, described in further detail below, performs this ** merge. ** @@ -100443,7 +101564,7 @@ struct MergeEngine { ** ** Essentially, this structure contains all those fields of the VdbeSorter ** structure for which each thread requires a separate instance. For example, -** each thread requries its own UnpackedRecord object to unpack records in +** each thread requeries its own UnpackedRecord object to unpack records in ** as part of comparison operations. ** ** Before a background thread is launched, variable bDone is set to 0. Then, @@ -100515,7 +101636,7 @@ struct VdbeSorter { ** PMA, in sorted order. The next key to be read is cached in nKey/aKey. ** aKey might point into aMap or into aBuffer. If neither of those locations ** contain a contiguous representation of the key, then aAlloc is allocated -** and the key is copied into aAlloc and aKey is made to poitn to aAlloc. +** and the key is copied into aAlloc and aKey is made to point to aAlloc. ** ** pFd==0 at EOF. */ @@ -101886,7 +103007,7 @@ static int vdbeSorterFlushPMA(VdbeSorter *pSorter){ ** the background thread from a sub-tasks previous turn is still running, ** skip it. If the first (pSorter->nTask-1) sub-tasks are all still busy, ** fall back to using the final sub-task. The first (pSorter->nTask-1) - ** sub-tasks are prefered as they use background threads - the final + ** sub-tasks are preferred as they use background threads - the final ** sub-task uses the main thread. */ for(i=0; iiPrev + i + 1) % nWorker; @@ -102370,7 +103491,7 @@ static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode){ rc = vdbeMergeEngineInit(pTask, pIncr->pMerger, eMode); - /* Set up the required files for pIncr. A multi-theaded IncrMerge object + /* Set up the required files for pIncr. A multi-threaded IncrMerge object ** requires two temp files to itself, whereas a single-threaded object ** only requires a region of pTask->file2. */ if( rc==SQLITE_OK ){ @@ -103010,6 +104131,8 @@ static int bytecodevtabConnect( "p5 INT," "comment TEXT," "subprog TEXT," + "nexec INT," + "ncycle INT," "stmt HIDDEN" ");", @@ -103172,7 +104295,7 @@ static int bytecodevtabColumn( } } } - i += 10; + i += 20; } } switch( i ){ @@ -103222,16 +104345,31 @@ static int bytecodevtabColumn( } break; } - case 10: /* tables_used.type */ + +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + case 9: /* nexec */ + sqlite3_result_int(ctx, pOp->nExec); + break; + case 10: /* ncycle */ + sqlite3_result_int(ctx, pOp->nCycle); + break; +#else + case 9: /* nexec */ + case 10: /* ncycle */ + sqlite3_result_int(ctx, 0); + break; +#endif + + case 20: /* tables_used.type */ sqlite3_result_text(ctx, pCur->zType, -1, SQLITE_STATIC); break; - case 11: /* tables_used.schema */ + case 21: /* tables_used.schema */ sqlite3_result_text(ctx, pCur->zSchema, -1, SQLITE_STATIC); break; - case 12: /* tables_used.name */ + case 22: /* tables_used.name */ sqlite3_result_text(ctx, pCur->zName, -1, SQLITE_STATIC); break; - case 13: /* tables_used.wr */ + case 23: /* tables_used.wr */ sqlite3_result_int(ctx, pOp->opcode==OP_OpenWrite); break; } @@ -103305,7 +104443,7 @@ static int bytecodevtabBestIndex( int rc = SQLITE_CONSTRAINT; struct sqlite3_index_constraint *p; bytecodevtab *pVTab = (bytecodevtab*)tab; - int iBaseCol = pVTab->bTablesUsed ? 4 : 8; + int iBaseCol = pVTab->bTablesUsed ? 4 : 10; pIdxInfo->estimatedCost = (double)100; pIdxInfo->estimatedRows = 100; pIdxInfo->idxNum = 0; @@ -103876,7 +105014,7 @@ static int walkWindowList(Walker *pWalker, Window *pList, int bOneOnly){ ** The return value from this routine is WRC_Abort to abandon the tree walk ** and WRC_Continue to continue. */ -static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ +SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3WalkExprNN(Walker *pWalker, Expr *pExpr){ int rc; testcase( ExprHasProperty(pExpr, EP_TokenOnly) ); testcase( ExprHasProperty(pExpr, EP_Reduced) ); @@ -103885,7 +105023,9 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ if( rc ) return rc & WRC_Abort; if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ assert( pExpr->x.pList==0 || pExpr->pRight==0 ); - if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort; + if( pExpr->pLeft && sqlite3WalkExprNN(pWalker, pExpr->pLeft) ){ + return WRC_Abort; + } if( pExpr->pRight ){ assert( !ExprHasProperty(pExpr, EP_WinFunc) ); pExpr = pExpr->pRight; @@ -103909,7 +105049,7 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ return WRC_Continue; } SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){ - return pExpr ? walkExpr(pWalker,pExpr) : WRC_Continue; + return pExpr ? sqlite3WalkExprNN(pWalker,pExpr) : WRC_Continue; } /* @@ -104035,7 +105175,7 @@ SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){ } /* Increase the walkerDepth when entering a subquery, and -** descrease when leaving the subquery. +** decrease when leaving the subquery. */ SQLITE_PRIVATE int sqlite3WalkerDepthIncrease(Walker *pWalker, Select *pSelect){ UNUSED_PARAMETER(pSelect); @@ -105769,7 +106909,7 @@ static int resolveOrderGroupBy( } for(j=0; jpEList->nExpr; j++){ if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){ - /* Since this expresion is being changed into a reference + /* Since this expression is being changed into a reference ** to an identical expression in the result set, remove all Window ** objects belonging to the expression from the Select.pWin list. */ windowRemoveExprFromSelect(pSelect, pE); @@ -106092,7 +107232,8 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames( return SQLITE_ERROR; } #endif - sqlite3WalkExpr(&w, pExpr); + assert( pExpr!=0 ); + sqlite3WalkExprNN(&w, pExpr); #if SQLITE_MAX_EXPR_DEPTH>0 w.pParse->nHeight -= pExpr->nHeight; #endif @@ -106134,7 +107275,7 @@ SQLITE_PRIVATE int sqlite3ResolveExprListNames( return WRC_Abort; } #endif - sqlite3WalkExpr(&w, pExpr); + sqlite3WalkExprNN(&w, pExpr); #if SQLITE_MAX_EXPR_DEPTH>0 w.pParse->nHeight -= pExpr->nHeight; #endif @@ -106156,7 +107297,7 @@ SQLITE_PRIVATE int sqlite3ResolveExprListNames( /* ** Resolve all names in all expressions of a SELECT and in all -** decendents of the SELECT, including compounds off of p->pPrior, +** descendants of the SELECT, including compounds off of p->pPrior, ** subqueries in expressions, and subqueries used as FROM clause ** terms. ** @@ -106306,6 +107447,7 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){ if( op==TK_SELECT_COLUMN ){ assert( pExpr->pLeft!=0 && ExprUseXSelect(pExpr->pLeft) ); assert( pExpr->iColumn < pExpr->iTable ); + assert( pExpr->iColumn >= 0 ); assert( pExpr->iTable==pExpr->pLeft->x.pSelect->pEList->nExpr ); return sqlite3ExprAffinity( pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr @@ -106542,7 +107684,7 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){ /* ** Return the collation sequence for the expression pExpr. If ** there is no defined collating sequence, return a pointer to the -** defautl collation sequence. +** default collation sequence. ** ** See also: sqlite3ExprCollSeq() ** @@ -106672,7 +107814,7 @@ SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq( return pColl; } -/* Expresssion p is a comparison operator. Return a collation sequence +/* Expression p is a comparison operator. Return a collation sequence ** appropriate for the comparison operator. ** ** This is normally just a wrapper around sqlite3BinaryCompareCollSeq(). @@ -107128,6 +108270,15 @@ SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ #define exprSetHeight(y) #endif /* SQLITE_MAX_EXPR_DEPTH>0 */ +/* +** Set the error offset for an Expr node, if possible. +*/ +SQLITE_PRIVATE void sqlite3ExprSetErrorOffset(Expr *pExpr, int iOfst){ + if( pExpr==0 ) return; + if( NEVER(ExprUseWJoin(pExpr)) ) return; + pExpr->w.iOfst = iOfst; +} + /* ** This routine is the core allocator for Expr nodes. ** @@ -107588,7 +108739,7 @@ SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){ /* ** Arrange to cause pExpr to be deleted when the pParse is deleted. ** This is similar to sqlite3ExprDelete() except that the delete is -** deferred untilthe pParse is deleted. +** deferred until the pParse is deleted. ** ** The pExpr might be deleted immediately on an OOM error. ** @@ -108430,7 +109581,7 @@ SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr *pExpr){ ** and 0 if it is FALSE. */ SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr *pExpr){ - pExpr = sqlite3ExprSkipCollate((Expr*)pExpr); + pExpr = sqlite3ExprSkipCollateAndLikely((Expr*)pExpr); assert( pExpr->op==TK_TRUEFALSE ); assert( !ExprHasProperty(pExpr, EP_IntValue) ); assert( sqlite3StrICmp(pExpr->u.zToken,"true")==0 @@ -109023,7 +110174,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ ** IN_INDEX_INDEX_ASC - The cursor was opened on an ascending index. ** IN_INDEX_INDEX_DESC - The cursor was opened on a descending index. ** IN_INDEX_EPH - The cursor was opened on a specially created and -** populated epheremal table. +** populated ephemeral table. ** IN_INDEX_NOOP - No cursor was allocated. The IN operator must be ** implemented as a sequence of comparisons. ** @@ -109036,7 +110187,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ ** an ephemeral table might need to be generated from the RHS and then ** pX->iTable made to point to the ephemeral table instead of an ** existing table. In this case, the creation and initialization of the -** ephmeral table might be put inside of a subroutine, the EP_Subrtn flag +** ephemeral table might be put inside of a subroutine, the EP_Subrtn flag ** will be set on pX and the pX->y.sub fields will be set to show where ** the subroutine is coded. ** @@ -109048,12 +110199,12 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ ** ** When IN_INDEX_LOOP is used (and the b-tree will be used to iterate ** through the set members) then the b-tree must not contain duplicates. -** An epheremal table will be created unless the selected columns are guaranteed +** An ephemeral table will be created unless the selected columns are guaranteed ** to be unique - either because it is an INTEGER PRIMARY KEY or due to ** a UNIQUE constraint or index. ** ** When IN_INDEX_MEMBERSHIP is used (and the b-tree will be used -** for fast set membership tests) then an epheremal table must +** for fast set membership tests) then an ephemeral table must ** be used unless is a single INTEGER PRIMARY KEY column or an ** index can be found with the specified as its left-most. ** @@ -109386,7 +110537,7 @@ SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){ ** x IN (SELECT a FROM b) -- IN operator with subquery on the right ** ** The pExpr parameter is the IN operator. The cursor number for the -** constructed ephermeral table is returned. The first time the ephemeral +** constructed ephemeral table is returned. The first time the ephemeral ** table is computed, the cursor number is also stored in pExpr->iTable, ** however the cursor number returned might not be the same, as it might ** have been duplicated using OP_OpenDup. @@ -110201,10 +111352,13 @@ SQLITE_PRIVATE int sqlite3ExprCodeGetColumn( u8 p5 /* P5 value for OP_Column + FLAGS */ ){ assert( pParse->pVdbe!=0 ); + assert( (p5 & (OPFLAG_NOCHNG|OPFLAG_TYPEOFARG|OPFLAG_LENGTHARG))==p5 ); + assert( IsVirtual(pTab) || (p5 & OPFLAG_NOCHNG)==0 ); sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pTab, iTable, iColumn, iReg); if( p5 ){ VdbeOp *pOp = sqlite3VdbeGetLastOp(pParse->pVdbe); if( pOp->opcode==OP_Column ) pOp->p5 = p5; + if( pOp->opcode==OP_VColumn ) pOp->p5 = (p5 & OPFLAG_NOCHNG); } return iReg; } @@ -110233,7 +111387,7 @@ static void exprToRegister(Expr *pExpr, int iReg){ /* ** Evaluate an expression (either a vector or a scalar expression) and store -** the result in continguous temporary registers. Return the index of +** the result in contiguous temporary registers. Return the index of ** the first register used to store the result. ** ** If the returned result register is a temporary scalar, then also write @@ -110273,7 +111427,7 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){ */ static void setDoNotMergeFlagOnCopy(Vdbe *v){ if( sqlite3VdbeGetLastOp(v)->opcode==OP_Copy ){ - sqlite3VdbeChangeP5(v, 1); /* Tag trailing OP_Copy as not mergable */ + sqlite3VdbeChangeP5(v, 1); /* Tag trailing OP_Copy as not mergeable */ } } @@ -110363,13 +111517,13 @@ static int exprCodeInlineFunction( } case INLINEFUNC_implies_nonnull_row: { - /* REsult of sqlite3ExprImpliesNonNullRow() */ + /* Result of sqlite3ExprImpliesNonNullRow() */ Expr *pA1; assert( nFarg==2 ); pA1 = pFarg->a[1].pExpr; if( pA1->op==TK_COLUMN ){ sqlite3VdbeAddOp2(v, OP_Integer, - sqlite3ExprImpliesNonNullRow(pFarg->a[0].pExpr,pA1->iTable), + sqlite3ExprImpliesNonNullRow(pFarg->a[0].pExpr,pA1->iTable,1), target); }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, target); @@ -110545,7 +111699,7 @@ expr_code_doover: if( ExprHasProperty(pExpr, EP_FixedCol) ){ /* This COLUMN expression is really a constant due to WHERE clause ** constraints, and that constant is coded by the pExpr->pLeft - ** expresssion. However, make sure the constant has the correct + ** expression. However, make sure the constant has the correct ** datatype by applying the Affinity of the table column to the ** constant. */ @@ -110871,7 +112025,7 @@ expr_code_doover: sqlite3ErrorMsg(pParse, "unknown function: %#T()", pExpr); break; } - if( pDef->funcFlags & SQLITE_FUNC_INLINE ){ + if( (pDef->funcFlags & SQLITE_FUNC_INLINE)!=0 && ALWAYS(pFarg!=0) ){ assert( (pDef->funcFlags & SQLITE_FUNC_UNSAFE)==0 ); assert( (pDef->funcFlags & SQLITE_FUNC_DIRECT)==0 ); return exprCodeInlineFunction(pParse, pFarg, @@ -110897,10 +112051,10 @@ expr_code_doover: r1 = sqlite3GetTempRange(pParse, nFarg); } - /* For length() and typeof() functions with a column argument, + /* For length() and typeof() and octet_length() functions, ** set the P5 parameter to the OP_Column opcode to OPFLAG_LENGTHARG - ** or OPFLAG_TYPEOFARG respectively, to avoid unnecessary data - ** loading. + ** or OPFLAG_TYPEOFARG or OPFLAG_BYTELENARG respectively, to avoid + ** unnecessary data loading. */ if( (pDef->funcFlags & (SQLITE_FUNC_LENGTH|SQLITE_FUNC_TYPEOF))!=0 ){ u8 exprOp; @@ -110910,14 +112064,16 @@ expr_code_doover: if( exprOp==TK_COLUMN || exprOp==TK_AGG_COLUMN ){ assert( SQLITE_FUNC_LENGTH==OPFLAG_LENGTHARG ); assert( SQLITE_FUNC_TYPEOF==OPFLAG_TYPEOFARG ); - testcase( pDef->funcFlags & OPFLAG_LENGTHARG ); - pFarg->a[0].pExpr->op2 = - pDef->funcFlags & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG); + assert( SQLITE_FUNC_BYTELEN==OPFLAG_BYTELENARG ); + assert( (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG)==OPFLAG_BYTELENARG ); + testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_LENGTHARG ); + testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_TYPEOFARG ); + testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_BYTELENARG); + pFarg->a[0].pExpr->op2 = pDef->funcFlags & OPFLAG_BYTELENARG; } } - sqlite3ExprCodeExprList(pParse, pFarg, r1, 0, - SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR); + sqlite3ExprCodeExprList(pParse, pFarg, r1, 0, SQLITE_ECEL_FACTOR); }else{ r1 = 0; } @@ -111274,7 +112430,7 @@ expr_code_doover: ** ** If regDest>=0 then the result is always stored in that register and the ** result is not reusable. If regDest<0 then this routine is free to -** store the value whereever it wants. The register where the expression +** store the value wherever it wants. The register where the expression ** is stored is returned. When regDest<0, two identical expressions might ** code to the same register, if they do not contain function calls and hence ** are factored out into the initialization section at the end of the @@ -112192,7 +113348,7 @@ static int exprImpliesNotNull( ** pE1: x!=123 pE2: x IS NOT NULL Result: true ** pE1: x!=?1 pE2: x IS NOT NULL Result: true ** pE1: x IS NULL pE2: x IS NOT NULL Result: false -** pE1: x IS ?2 pE2: x IS NOT NULL Reuslt: false +** pE1: x IS ?2 pE2: x IS NOT NULL Result: false ** ** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has ** Expr.iTable<0 then assume a table number given by iTab. @@ -112229,11 +113385,29 @@ SQLITE_PRIVATE int sqlite3ExprImpliesExpr( return 0; } +/* This is a helper function to impliesNotNullRow(). In this routine, +** set pWalker->eCode to one only if *both* of the input expressions +** separately have the implies-not-null-row property. +*/ +static void bothImplyNotNullRow(Walker *pWalker, Expr *pE1, Expr *pE2){ + if( pWalker->eCode==0 ){ + sqlite3WalkExpr(pWalker, pE1); + if( pWalker->eCode ){ + pWalker->eCode = 0; + sqlite3WalkExpr(pWalker, pE2); + } + } +} + /* ** This is the Expr node callback for sqlite3ExprImpliesNonNullRow(). ** If the expression node requires that the table at pWalker->iCur ** have one or more non-NULL column, then set pWalker->eCode to 1 and abort. ** +** pWalker->mWFlags is non-zero if this inquiry is being undertaking on +** behalf of a RIGHT JOIN (or FULL JOIN). That makes a difference when +** evaluating terms in the ON clause of an inner join. +** ** This routine controls an optimization. False positives (setting ** pWalker->eCode to 1 when it should not be) are deadly, but false-negatives ** (never setting pWalker->eCode) is a harmless missed optimization. @@ -112242,28 +113416,33 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ testcase( pExpr->op==TK_AGG_COLUMN ); testcase( pExpr->op==TK_AGG_FUNCTION ); if( ExprHasProperty(pExpr, EP_OuterON) ) return WRC_Prune; + if( ExprHasProperty(pExpr, EP_InnerON) && pWalker->mWFlags ){ + /* If iCur is used in an inner-join ON clause to the left of a + ** RIGHT JOIN, that does *not* mean that the table must be non-null. + ** But it is difficult to check for that condition precisely. + ** To keep things simple, any use of iCur from any inner-join is + ** ignored while attempting to simplify a RIGHT JOIN. */ + return WRC_Prune; + } switch( pExpr->op ){ case TK_ISNOT: case TK_ISNULL: case TK_NOTNULL: case TK_IS: - case TK_OR: case TK_VECTOR: - case TK_CASE: - case TK_IN: case TK_FUNCTION: case TK_TRUTH: + case TK_CASE: testcase( pExpr->op==TK_ISNOT ); testcase( pExpr->op==TK_ISNULL ); testcase( pExpr->op==TK_NOTNULL ); testcase( pExpr->op==TK_IS ); - testcase( pExpr->op==TK_OR ); testcase( pExpr->op==TK_VECTOR ); - testcase( pExpr->op==TK_CASE ); - testcase( pExpr->op==TK_IN ); testcase( pExpr->op==TK_FUNCTION ); testcase( pExpr->op==TK_TRUTH ); + testcase( pExpr->op==TK_CASE ); return WRC_Prune; + case TK_COLUMN: if( pWalker->u.iCur==pExpr->iTable ){ pWalker->eCode = 1; @@ -112271,21 +113450,38 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ } return WRC_Prune; + case TK_OR: case TK_AND: - if( pWalker->eCode==0 ){ + /* Both sides of an AND or OR must separately imply non-null-row. + ** Consider these cases: + ** 1. NOT (x AND y) + ** 2. x OR y + ** If only one of x or y is non-null-row, then the overall expression + ** can be true if the other arm is false (case 1) or true (case 2). + */ + testcase( pExpr->op==TK_OR ); + testcase( pExpr->op==TK_AND ); + bothImplyNotNullRow(pWalker, pExpr->pLeft, pExpr->pRight); + return WRC_Prune; + + case TK_IN: + /* Beware of "x NOT IN ()" and "x NOT IN (SELECT 1 WHERE false)", + ** both of which can be true. But apart from these cases, if + ** the left-hand side of the IN is NULL then the IN itself will be + ** NULL. */ + if( ExprUseXList(pExpr) && ALWAYS(pExpr->x.pList->nExpr>0) ){ sqlite3WalkExpr(pWalker, pExpr->pLeft); - if( pWalker->eCode ){ - pWalker->eCode = 0; - sqlite3WalkExpr(pWalker, pExpr->pRight); - } } return WRC_Prune; case TK_BETWEEN: - if( sqlite3WalkExpr(pWalker, pExpr->pLeft)==WRC_Abort ){ - assert( pWalker->eCode ); - return WRC_Abort; - } + /* In "x NOT BETWEEN y AND z" either x must be non-null-row or else + ** both y and z must be non-null row */ + assert( ExprUseXList(pExpr) ); + assert( pExpr->x.pList->nExpr==2 ); + sqlite3WalkExpr(pWalker, pExpr->pLeft); + bothImplyNotNullRow(pWalker, pExpr->x.pList->a[0].pExpr, + pExpr->x.pList->a[1].pExpr); return WRC_Prune; /* Virtual tables are allowed to use constraints like x=NULL. So @@ -112347,7 +113543,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ ** be non-NULL, then the LEFT JOIN can be safely converted into an ** ordinary join. */ -SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ +SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab, int isRJ){ Walker w; p = sqlite3ExprSkipCollateAndLikely(p); if( p==0 ) return 0; @@ -112355,7 +113551,7 @@ SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ p = p->pLeft; }else{ while( p->op==TK_AND ){ - if( sqlite3ExprImpliesNonNullRow(p->pLeft, iTab) ) return 1; + if( sqlite3ExprImpliesNonNullRow(p->pLeft, iTab, isRJ) ) return 1; p = p->pRight; } } @@ -112363,6 +113559,7 @@ SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ w.xSelectCallback = 0; w.xSelectCallback2 = 0; w.eCode = 0; + w.mWFlags = isRJ!=0; w.u.iCur = iTab; sqlite3WalkExpr(&w, p); return w.eCode; @@ -112423,7 +113620,7 @@ SQLITE_PRIVATE int sqlite3ExprCoveredByIndex( } -/* Structure used to pass information throught the Walker in order to +/* Structure used to pass information throughout the Walker in order to ** implement sqlite3ReferencesSrcList(). */ struct RefSrcList { @@ -112639,7 +113836,7 @@ static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){ ** Return the index in aCol[] of the entry that describes that column. ** ** If no prior entry is found, create a new one and return -1. The -** new column will have an idex of pAggInfo->nColumn-1. +** new column will have an index of pAggInfo->nColumn-1. */ static void findOrCreateAggInfoColumn( Parse *pParse, /* Parsing context */ @@ -112652,6 +113849,7 @@ static void findOrCreateAggInfoColumn( assert( pAggInfo->iFirstReg==0 ); pCol = pAggInfo->aCol; for(k=0; knColumn; k++, pCol++){ + if( pCol->pCExpr==pExpr ) return; if( pCol->iTable==pExpr->iTable && pCol->iColumn==pExpr->iColumn && pExpr->op!=TK_IF_NULL_ROW @@ -113532,7 +114730,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ pNew->u.tab.pDfltList = sqlite3ExprListDup(db, pTab->u.tab.pDfltList, 0); pNew->pSchema = db->aDb[iDb].pSchema; pNew->u.tab.addColOffset = pTab->u.tab.addColOffset; - pNew->nTabRef = 1; + assert( pNew->nTabRef==1 ); exit_begin_add_column: sqlite3SrcListDelete(db, pSrc); @@ -114037,7 +115235,7 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){ } /* -** An error occured while parsing or otherwise processing a database +** An error occurred while parsing or otherwise processing a database ** object (either pParse->pNewTable, pNewIndex or pNewTrigger) as part of an ** ALTER TABLE RENAME COLUMN program. The error message emitted by the ** sub-routine is currently stored in pParse->zErrMsg. This function @@ -117143,14 +118341,15 @@ static int loadStatTbl( decodeIntArray((char*)sqlite3_column_text(pStmt,2),nCol,pSample->anLt,0,0); decodeIntArray((char*)sqlite3_column_text(pStmt,3),nCol,pSample->anDLt,0,0); - /* Take a copy of the sample. Add two 0x00 bytes the end of the buffer. + /* Take a copy of the sample. Add 8 extra 0x00 bytes the end of the buffer. ** This is in case the sample record is corrupted. In that case, the ** sqlite3VdbeRecordCompare() may read up to two varints past the ** end of the allocated buffer before it realizes it is dealing with - ** a corrupt record. Adding the two 0x00 bytes prevents this from causing + ** a corrupt record. Or it might try to read a large integer from the + ** buffer. In any case, eight 0x00 bytes prevents this from causing ** a buffer overread. */ pSample->n = sqlite3_column_bytes(pStmt, 4); - pSample->p = sqlite3DbMallocZero(db, pSample->n + 2); + pSample->p = sqlite3DbMallocZero(db, pSample->n + 8); if( pSample->p==0 ){ sqlite3_finalize(pStmt); return SQLITE_NOMEM_BKPT; @@ -118108,7 +119307,7 @@ SQLITE_PRIVATE int sqlite3AuthCheck( sqlite3 *db = pParse->db; int rc; - /* Don't do any authorization checks if the database is initialising + /* Don't do any authorization checks if the database is initializing ** or if the parser is being invoked from within sqlite3_declare_vtab. */ assert( !IN_RENAME_OBJECT || db->xAuth==0 ); @@ -118409,15 +119608,17 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ pParse->nVtabLock = 0; #endif +#ifndef SQLITE_OMIT_SHARED_CACHE /* Once all the cookies have been verified and transactions opened, ** obtain the required table-locks. This is a no-op unless the ** shared-cache feature is enabled. */ - codeTableLocks(pParse); + if( pParse->nTableLock ) codeTableLocks(pParse); +#endif /* Initialize any AUTOINCREMENT data structures required. */ - sqlite3AutoincrementBegin(pParse); + if( pParse->pAinc ) sqlite3AutoincrementBegin(pParse); /* Code constant expressions that where factored out of inner loops. ** @@ -118930,7 +120131,7 @@ SQLITE_PRIVATE void sqlite3ColumnSetColl( } /* -** Return the collating squence name for a column +** Return the collating sequence name for a column */ SQLITE_PRIVATE const char *sqlite3ColumnColl(Column *pCol){ const char *z; @@ -119688,7 +120889,7 @@ SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token sName, Token sType){ } if( !IN_RENAME_OBJECT ) sqlite3DequoteToken(&sName); - /* Because keywords GENERATE ALWAYS can be converted into indentifiers + /* Because keywords GENERATE ALWAYS can be converted into identifiers ** by the parser, we can sometimes end up with a typename that ends ** with "generated always". Check for this case and omit the surplus ** text. */ @@ -119909,7 +121110,7 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue( Parse *pParse, /* Parsing context */ Expr *pExpr, /* The parsed expression of the default value */ const char *zStart, /* Start of the default value text */ - const char *zEnd /* First character past end of defaut value text */ + const char *zEnd /* First character past end of default value text */ ){ Table *p; Column *pCol; @@ -120257,7 +121458,7 @@ static int identLength(const char *z){ ** to the specified offset in the buffer and updates *pIdx to refer ** to the first byte after the last byte written before returning. ** -** If the string zSignedIdent consists entirely of alpha-numeric +** If the string zSignedIdent consists entirely of alphanumeric ** characters, does not begin with a digit and is not an SQL keyword, ** then it is copied to the output buffer exactly as it is. Otherwise, ** it is quoted using double-quotes. @@ -120409,7 +121610,7 @@ static void estimateIndexWidth(Index *pIdx){ for(i=0; inColumn; i++){ i16 x = pIdx->aiColumn[i]; assert( xpTable->nCol ); - wIndex += x<0 ? 1 : aCol[pIdx->aiColumn[i]].szEst; + wIndex += x<0 ? 1 : aCol[x].szEst; } pIdx->szIdxRow = sqlite3LogEst(wIndex*4); } @@ -122147,7 +123348,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( #ifndef SQLITE_OMIT_TEMPDB /* If the index name was unqualified, check if the table ** is a temp table. If so, set the database to 1. Do not do this - ** if initialising a database schema. + ** if initializing a database schema. */ if( !db->init.busy ){ pTab = sqlite3SrcListLookup(pParse, pTblName); @@ -123804,7 +125005,7 @@ SQLITE_PRIVATE void sqlite3CteDelete(sqlite3 *db, Cte *pCte){ /* ** This routine is invoked once per CTE by the parser while parsing a -** WITH clause. The CTE described by teh third argument is added to +** WITH clause. The CTE described by the third argument is added to ** the WITH clause of the second argument. If the second argument is ** NULL, then a new WITH argument is created. */ @@ -124446,8 +125647,9 @@ SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ Table *pTab; assert( pItem && pSrc->nSrc>=1 ); pTab = sqlite3LocateTableItem(pParse, 0, pItem); - sqlite3DeleteTable(pParse->db, pItem->pTab); + if( pItem->pTab ) sqlite3DeleteTable(pParse->db, pItem->pTab); pItem->pTab = pTab; + pItem->fg.notCte = 1; if( pTab ){ pTab->nTabRef++; if( pItem->fg.isIndexedBy && sqlite3IndexedByLookup(pParse, pItem) ){ @@ -124600,7 +125802,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( sqlite3 *db = pParse->db; Expr *pLhs = NULL; /* LHS of IN(SELECT...) operator */ Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */ - ExprList *pEList = NULL; /* Expression list contaning only pSelectRowid */ + ExprList *pEList = NULL; /* Expression list containing only pSelectRowid*/ SrcList *pSelectSrc = NULL; /* SELECT rowid FROM x ... (dup of pSrc) */ Select *pSelect = NULL; /* Complete SELECT tree */ Table *pTab; @@ -124638,14 +125840,20 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( ); }else{ Index *pPk = sqlite3PrimaryKeyIndex(pTab); + assert( pPk!=0 ); + assert( pPk->nKeyCol>=1 ); if( pPk->nKeyCol==1 ){ - const char *zName = pTab->aCol[pPk->aiColumn[0]].zCnName; + const char *zName; + assert( pPk->aiColumn[0]>=0 && pPk->aiColumn[0]nCol ); + zName = pTab->aCol[pPk->aiColumn[0]].zCnName; pLhs = sqlite3Expr(db, TK_ID, zName); pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, zName)); }else{ int i; for(i=0; inKeyCol; i++){ - Expr *p = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zCnName); + Expr *p; + assert( pPk->aiColumn[i]>=0 && pPk->aiColumn[i]nCol ); + p = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zCnName); pEList = sqlite3ExprListAppend(pParse, pEList, p); } pLhs = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); @@ -124674,7 +125882,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( pOrderBy,0,pLimit ); - /* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */ + /* now generate the new WHERE rowid IN clause for the DELETE/UPDATE */ pInClause = sqlite3PExpr(pParse, TK_IN, pLhs, 0); sqlite3PExprAddSelect(pParse, pInClause, pSelect); return pInClause; @@ -124903,7 +126111,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( if( HasRowid(pTab) ){ /* For a rowid table, initialize the RowSet to an empty set */ pPk = 0; - nPk = 1; + assert( nPk==1 ); iRowSet = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); }else{ @@ -124931,7 +126139,8 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( if( pWInfo==0 ) goto delete_from_cleanup; eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI ); - assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF ); + assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF + || OptimizationDisabled(db, SQLITE_OnePass) ); if( eOnePass!=ONEPASS_SINGLE ) sqlite3MultiWrite(pParse); if( sqlite3WhereUsesDeferredSeek(pWInfo) ){ sqlite3VdbeAddOp1(v, OP_FinishSeek, iTabCur); @@ -125268,9 +126477,11 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete( sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0); /* Invoke AFTER DELETE trigger programs. */ - sqlite3CodeRowTrigger(pParse, pTrigger, - TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel - ); + if( pTrigger ){ + sqlite3CodeRowTrigger(pParse, pTrigger, + TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel + ); + } /* Jump here if the row had already been deleted before any BEFORE ** trigger programs were invoked. Or if a trigger program throws a @@ -125583,6 +126794,42 @@ static void lengthFunc( } } +/* +** Implementation of the octet_length() function +*/ +static void bytelengthFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + assert( argc==1 ); + UNUSED_PARAMETER(argc); + switch( sqlite3_value_type(argv[0]) ){ + case SQLITE_BLOB: { + sqlite3_result_int(context, sqlite3_value_bytes(argv[0])); + break; + } + case SQLITE_INTEGER: + case SQLITE_FLOAT: { + i64 m = sqlite3_context_db_handle(context)->enc<=SQLITE_UTF8 ? 1 : 2; + sqlite3_result_int64(context, sqlite3_value_bytes(argv[0])*m); + break; + } + case SQLITE_TEXT: { + if( sqlite3_value_encoding(argv[0])<=SQLITE_UTF8 ){ + sqlite3_result_int(context, sqlite3_value_bytes(argv[0])); + }else{ + sqlite3_result_int(context, sqlite3_value_bytes16(argv[0])); + } + break; + } + default: { + sqlite3_result_null(context); + break; + } + } +} + /* ** Implementation of the abs() function. ** @@ -125859,7 +127106,7 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ }else if( n==0 ){ r = (double)((sqlite_int64)(r+(r<0?-0.5:+0.5))); }else{ - zBuf = sqlite3_mprintf("%.*f",n,r); + zBuf = sqlite3_mprintf("%!.*f",n,r); if( zBuf==0 ){ sqlite3_result_error_nomem(context); return; @@ -126059,7 +127306,7 @@ struct compareInfo { /* ** For LIKE and GLOB matching on EBCDIC machines, assume that every -** character is exactly one byte in size. Also, provde the Utf8Read() +** character is exactly one byte in size. Also, provide the Utf8Read() ** macro for fast reading of the next character in the common case where ** the next character is ASCII. */ @@ -126292,7 +127539,7 @@ SQLITE_API int sqlite3_like_count = 0; /* ** Implementation of the like() SQL function. This function implements -** the build-in LIKE operator. The first argument to the function is the +** the built-in LIKE operator. The first argument to the function is the ** pattern and the second argument is the string. So, the SQL statements: ** ** A LIKE B @@ -126625,6 +127872,7 @@ static void charFunc( *zOut++ = 0x80 + (u8)(c & 0x3F); } \ } + *zOut = 0; sqlite3_result_text64(context, (char*)z, zOut-z, sqlite3_free, SQLITE_UTF8); } @@ -126678,7 +127926,7 @@ static int strContainsChar(const u8 *zStr, int nStr, u32 ch){ ** decoded and returned as a blob. ** ** If there is only a single argument, then it must consist only of an -** even number of hexadeximal digits. Otherwise, return NULL. +** even number of hexadecimal digits. Otherwise, return NULL. ** ** Or, if there is a second argument, then any character that appears in ** the second argument is also allowed to appear between pairs of hexadecimal @@ -127068,13 +128316,68 @@ static void loadExt(sqlite3_context *context, int argc, sqlite3_value **argv){ */ typedef struct SumCtx SumCtx; struct SumCtx { - double rSum; /* Floating point sum */ - i64 iSum; /* Integer sum */ + double rSum; /* Running sum as as a double */ + double rErr; /* Error term for Kahan-Babushka-Neumaier summation */ + i64 iSum; /* Running sum as a signed integer */ i64 cnt; /* Number of elements summed */ - u8 overflow; /* True if integer overflow seen */ - u8 approx; /* True if non-integer value was input to the sum */ + u8 approx; /* True if any non-integer value was input to the sum */ + u8 ovrfl; /* Integer overflow seen */ }; +/* +** Do one step of the Kahan-Babushka-Neumaier summation. +** +** https://en.wikipedia.org/wiki/Kahan_summation_algorithm +** +** Variables are marked "volatile" to defeat c89 x86 floating point +** optimizations can mess up this algorithm. +*/ +static void kahanBabuskaNeumaierStep( + volatile SumCtx *pSum, + volatile double r +){ + volatile double s = pSum->rSum; + volatile double t = s + r; + if( fabs(s) > fabs(r) ){ + pSum->rErr += (s - t) + r; + }else{ + pSum->rErr += (r - t) + s; + } + pSum->rSum = t; +} + +/* +** Add a (possibly large) integer to the running sum. +*/ +static void kahanBabuskaNeumaierStepInt64(volatile SumCtx *pSum, i64 iVal){ + if( iVal<=-4503599627370496LL || iVal>=+4503599627370496LL ){ + i64 iBig, iSm; + iSm = iVal % 16384; + iBig = iVal - iSm; + kahanBabuskaNeumaierStep(pSum, iBig); + kahanBabuskaNeumaierStep(pSum, iSm); + }else{ + kahanBabuskaNeumaierStep(pSum, (double)iVal); + } +} + +/* +** Initialize the Kahan-Babaska-Neumaier sum from a 64-bit integer +*/ +static void kahanBabuskaNeumaierInit( + volatile SumCtx *p, + i64 iVal +){ + if( iVal<=-4503599627370496LL || iVal>=+4503599627370496LL ){ + i64 iSm = iVal % 16384; + p->rSum = (double)(iVal - iSm); + p->rErr = (double)iSm; + }else{ + p->rSum = (double)iVal; + p->rErr = 0.0; + } +} + /* ** Routines used to compute the sum, average, and total. ** @@ -127094,15 +128397,29 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ type = sqlite3_value_numeric_type(argv[0]); if( p && type!=SQLITE_NULL ){ p->cnt++; - if( type==SQLITE_INTEGER ){ - i64 v = sqlite3_value_int64(argv[0]); - p->rSum += v; - if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, v) ){ - p->approx = p->overflow = 1; + if( p->approx==0 ){ + if( type!=SQLITE_INTEGER ){ + kahanBabuskaNeumaierInit(p, p->iSum); + p->approx = 1; + kahanBabuskaNeumaierStep(p, sqlite3_value_double(argv[0])); + }else{ + i64 x = p->iSum; + if( sqlite3AddInt64(&x, sqlite3_value_int64(argv[0]))==0 ){ + p->iSum = x; + }else{ + p->ovrfl = 1; + kahanBabuskaNeumaierInit(p, p->iSum); + p->approx = 1; + kahanBabuskaNeumaierStepInt64(p, sqlite3_value_int64(argv[0])); + } } }else{ - p->rSum += sqlite3_value_double(argv[0]); - p->approx = 1; + if( type==SQLITE_INTEGER ){ + kahanBabuskaNeumaierStepInt64(p, sqlite3_value_int64(argv[0])); + }else{ + p->ovrfl = 0; + kahanBabuskaNeumaierStep(p, sqlite3_value_double(argv[0])); + } } } } @@ -127119,13 +128436,18 @@ static void sumInverse(sqlite3_context *context, int argc, sqlite3_value**argv){ if( ALWAYS(p) && type!=SQLITE_NULL ){ assert( p->cnt>0 ); p->cnt--; - assert( type==SQLITE_INTEGER || p->approx ); - if( type==SQLITE_INTEGER && p->approx==0 ){ - i64 v = sqlite3_value_int64(argv[0]); - p->rSum -= v; - p->iSum -= v; + if( !p->approx ){ + p->iSum -= sqlite3_value_int64(argv[0]); + }else if( type==SQLITE_INTEGER ){ + i64 iVal = sqlite3_value_int64(argv[0]); + if( iVal!=SMALLEST_INT64 ){ + kahanBabuskaNeumaierStepInt64(p, -iVal); + }else{ + kahanBabuskaNeumaierStepInt64(p, LARGEST_INT64); + kahanBabuskaNeumaierStepInt64(p, 1); + } }else{ - p->rSum -= sqlite3_value_double(argv[0]); + kahanBabuskaNeumaierStep(p, -sqlite3_value_double(argv[0])); } } } @@ -127136,10 +128458,14 @@ static void sumFinalize(sqlite3_context *context){ SumCtx *p; p = sqlite3_aggregate_context(context, 0); if( p && p->cnt>0 ){ - if( p->overflow ){ - sqlite3_result_error(context,"integer overflow",-1); - }else if( p->approx ){ - sqlite3_result_double(context, p->rSum); + if( p->approx ){ + if( p->ovrfl ){ + sqlite3_result_error(context,"integer overflow",-1); + }else if( !sqlite3IsNaN(p->rErr) ){ + sqlite3_result_double(context, p->rSum+p->rErr); + }else{ + sqlite3_result_double(context, p->rSum); + } }else{ sqlite3_result_int64(context, p->iSum); } @@ -127149,14 +128475,29 @@ static void avgFinalize(sqlite3_context *context){ SumCtx *p; p = sqlite3_aggregate_context(context, 0); if( p && p->cnt>0 ){ - sqlite3_result_double(context, p->rSum/(double)p->cnt); + double r; + if( p->approx ){ + r = p->rSum; + if( !sqlite3IsNaN(p->rErr) ) r += p->rErr; + }else{ + r = (double)(p->iSum); + } + sqlite3_result_double(context, r/(double)p->cnt); } } static void totalFinalize(sqlite3_context *context){ SumCtx *p; + double r = 0.0; p = sqlite3_aggregate_context(context, 0); - /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ - sqlite3_result_double(context, p ? p->rSum : (double)0); + if( p ){ + if( p->approx ){ + r = p->rSum; + if( !sqlite3IsNaN(p->rErr) ) r += p->rErr; + }else{ + r = (double)(p->iSum); + } + } + sqlite3_result_double(context, r); } /* @@ -127378,7 +128719,7 @@ static void groupConcatInverse( if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC)); /* pGCC is always non-NULL since groupConcatStep() will have always - ** run frist to initialize it */ + ** run first to initialize it */ if( ALWAYS(pGCC) ){ int nVS; /* Must call sqlite3_value_text() to convert the argument into text prior @@ -127462,8 +128803,10 @@ SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3 *db){ ** sensitive. */ SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ + FuncDef *pDef; struct compareInfo *pInfo; int flags; + int nArg; if( caseSensitive ){ pInfo = (struct compareInfo*)&likeInfoAlt; flags = SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE; @@ -127471,10 +128814,13 @@ SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive) pInfo = (struct compareInfo*)&likeInfoNorm; flags = SQLITE_FUNC_LIKE; } - sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0, 0, 0); - sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0, 0, 0); - sqlite3FindFunction(db, "like", 2, SQLITE_UTF8, 0)->funcFlags |= flags; - sqlite3FindFunction(db, "like", 3, SQLITE_UTF8, 0)->funcFlags |= flags; + for(nArg=2; nArg<=3; nArg++){ + sqlite3CreateFunc(db, "like", nArg, SQLITE_UTF8, pInfo, likeFunc, + 0, 0, 0, 0, 0); + pDef = sqlite3FindFunction(db, "like", nArg, SQLITE_UTF8, 0); + pDef->funcFlags |= flags; + pDef->funcFlags &= ~SQLITE_FUNC_UNSAFE; + } } /* @@ -127746,6 +129092,37 @@ static void signFunc( sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0); } +#ifdef SQLITE_DEBUG +/* +** Implementation of fpdecode(x,y,z) function. +** +** x is a real number that is to be decoded. y is the precision. +** z is the maximum real precision. +*/ +static void fpdecodeFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + FpDecode s; + double x; + int y, z; + char zBuf[100]; + UNUSED_PARAMETER(argc); + assert( argc==3 ); + x = sqlite3_value_double(argv[0]); + y = sqlite3_value_int(argv[1]); + z = sqlite3_value_int(argv[2]); + sqlite3FpDecode(&s, x, y, z); + if( s.isSpecial==2 ){ + sqlite3_snprintf(sizeof(zBuf), zBuf, "NaN"); + }else{ + sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP); + } + sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); +} +#endif /* SQLITE_DEBUG */ + /* ** All of the FuncDef structures in the aBuiltinFunc[] array above ** to the global function hash table. This occurs at start-time (as @@ -127810,12 +129187,16 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){ FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), FUNCTION2(subtype, 1, 0, 0, subtypeFunc, SQLITE_FUNC_TYPEOF), FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH), + FUNCTION2(octet_length, 1, 0, 0, bytelengthFunc,SQLITE_FUNC_BYTELEN), FUNCTION(instr, 2, 0, 0, instrFunc ), FUNCTION(printf, -1, 0, 0, printfFunc ), FUNCTION(format, -1, 0, 0, printfFunc ), FUNCTION(unicode, 1, 0, 0, unicodeFunc ), FUNCTION(char, -1, 0, 0, charFunc ), FUNCTION(abs, 1, 0, 0, absFunc ), +#ifdef SQLITE_DEBUG + FUNCTION(fpdecode, 3, 0, 0, fpdecodeFunc ), +#endif #ifndef SQLITE_OMIT_FLOATING_POINT FUNCTION(round, 1, 0, 0, roundFunc ), FUNCTION(round, 2, 0, 0, roundFunc ), @@ -129386,9 +130767,8 @@ SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *db, Table *pTab){ if( pFKey->pPrevTo ){ pFKey->pPrevTo->pNextTo = pFKey->pNextTo; }else{ - void *p = (void *)pFKey->pNextTo; - const char *z = (p ? pFKey->pNextTo->zTo : pFKey->zTo); - sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, p); + const char *z = (pFKey->pNextTo ? pFKey->pNextTo->zTo : pFKey->zTo); + sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, pFKey->pNextTo); } if( pFKey->pNextTo ){ pFKey->pNextTo->pPrevTo = pFKey->pPrevTo; @@ -129451,8 +130831,10 @@ SQLITE_PRIVATE void sqlite3OpenTable( assert( pParse->pVdbe!=0 ); v = pParse->pVdbe; assert( opcode==OP_OpenWrite || opcode==OP_OpenRead ); - sqlite3TableLock(pParse, iDb, pTab->tnum, - (opcode==OP_OpenWrite)?1:0, pTab->zName); + if( !pParse->db->noSharedCache ){ + sqlite3TableLock(pParse, iDb, pTab->tnum, + (opcode==OP_OpenWrite)?1:0, pTab->zName); + } if( HasRowid(pTab) ){ sqlite3VdbeAddOp4Int(v, opcode, iCur, pTab->tnum, iDb, pTab->nNVCol); VdbeComment((v, "%s", pTab->zName)); @@ -129581,7 +130963,7 @@ SQLITE_PRIVATE char *sqlite3TableAffinityStr(sqlite3 *db, const Table *pTab){ ** For STRICT tables: ** ------------------ ** -** Generate an appropropriate OP_TypeCheck opcode that will verify the +** Generate an appropriate OP_TypeCheck opcode that will verify the ** datatypes against the column definitions in pTab. If iReg==0, that ** means an OP_MakeRecord opcode has already been generated and should be ** the last opcode generated. The new OP_TypeCheck needs to be inserted @@ -130873,7 +132255,7 @@ insert_cleanup: /* This is the Walker callback from sqlite3ExprReferencesUpdatedColumn(). * Set bit 0x01 of pWalker->eCode if pWalker->eCode to 0 and if this ** expression node references any of the -** columns that are being modifed by an UPDATE statement. +** columns that are being modified by an UPDATE statement. */ static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){ if( pExpr->op==TK_COLUMN ){ @@ -131096,7 +132478,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( int *aiChng, /* column i is unchanged if aiChng[i]<0 */ Upsert *pUpsert /* ON CONFLICT clauses, if any. NULL otherwise */ ){ - Vdbe *v; /* VDBE under constrution */ + Vdbe *v; /* VDBE under construction */ Index *pIdx; /* Pointer to one of the indices */ Index *pPk = 0; /* The PRIMARY KEY index for WITHOUT ROWID tables */ sqlite3 *db; /* Database connection */ @@ -131579,7 +132961,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( pIdx; pIdx = indexIteratorNext(&sIdxIter, &ix) ){ - int regIdx; /* Range of registers hold conent for pIdx */ + int regIdx; /* Range of registers holding content for pIdx */ int regR; /* Range of registers holding conflicting PK */ int iThisCur; /* Cursor for this UNIQUE index */ int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */ @@ -132074,6 +133456,8 @@ SQLITE_PRIVATE int sqlite3OpenTableAndIndices( assert( op==OP_OpenRead || op==OP_OpenWrite ); assert( op==OP_OpenWrite || p5==0 ); + assert( piDataCur!=0 ); + assert( piIdxCur!=0 ); if( IsVirtual(pTab) ){ /* This routine is a no-op for virtual tables. Leave the output ** variables *piDataCur and *piIdxCur set to illegal cursor numbers @@ -132086,18 +133470,18 @@ SQLITE_PRIVATE int sqlite3OpenTableAndIndices( assert( v!=0 ); if( iBase<0 ) iBase = pParse->nTab; iDataCur = iBase++; - if( piDataCur ) *piDataCur = iDataCur; + *piDataCur = iDataCur; if( HasRowid(pTab) && (aToOpen==0 || aToOpen[0]) ){ sqlite3OpenTable(pParse, iDataCur, iDb, pTab, op); - }else{ + }else if( pParse->db->noSharedCache==0 ){ sqlite3TableLock(pParse, iDb, pTab->tnum, op==OP_OpenWrite, pTab->zName); } - if( piIdxCur ) *piIdxCur = iBase; + *piIdxCur = iBase; for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ int iIdxCur = iBase++; assert( pIdx->pSchema==pTab->pSchema ); if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){ - if( piDataCur ) *piDataCur = iIdxCur; + *piDataCur = iIdxCur; p5 = 0; } if( aToOpen==0 || aToOpen[i+1] ){ @@ -132395,7 +133779,7 @@ static int xferOptimization( } #endif #ifndef SQLITE_OMIT_FOREIGN_KEY - /* Disallow the transfer optimization if the destination table constains + /* Disallow the transfer optimization if the destination table contains ** any foreign key constraints. This is more restrictive than necessary. ** But the main beneficiary of the transfer optimization is the VACUUM ** command, and the VACUUM command disables foreign key constraints. So @@ -133105,6 +134489,8 @@ struct sqlite3_api_routines { int (*value_encoding)(sqlite3_value*); /* Version 3.41.0 and later */ int (*is_interrupted)(sqlite3*); + /* Version 3.43.0 and later */ + int (*stmt_explain)(sqlite3_stmt*,int); }; /* @@ -133433,6 +134819,8 @@ typedef int (*sqlite3_loadext_entry)( #define sqlite3_value_encoding sqlite3_api->value_encoding /* Version 3.41.0 and later */ #define sqlite3_is_interrupted sqlite3_api->is_interrupted +/* Version 3.43.0 and later */ +#define sqlite3_stmt_explain sqlite3_api->stmt_explain #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) @@ -133949,7 +135337,9 @@ static const sqlite3_api_routines sqlite3Apis = { /* Version 3.40.0 and later */ sqlite3_value_encoding, /* Version 3.41.0 and later */ - sqlite3_is_interrupted + sqlite3_is_interrupted, + /* Version 3.43.0 and later */ + sqlite3_stmt_explain }; /* True if x is the directory separator character @@ -134029,6 +135419,10 @@ static int sqlite3LoadExtension( */ if( nMsg>SQLITE_MAX_PATHLEN ) goto extension_not_found; + /* Do not allow sqlite3_load_extension() to link to a copy of the + ** running application, by passing in an empty filename. */ + if( nMsg==0 ) goto extension_not_found; + handle = sqlite3OsDlOpen(pVfs, zFile); #if SQLITE_OS_UNIX || SQLITE_OS_WIN for(ii=0; iipParse; db->pParse = &sParse; sParse.db = db; - sParse.pReprepare = pReprepare; + if( pReprepare ){ + sParse.pReprepare = pReprepare; + sParse.explain = sqlite3_stmt_isexplain((sqlite3_stmt*)pReprepare); + }else{ + assert( sParse.pReprepare==0 ); + } assert( ppStmt && *ppStmt==0 ); if( db->mallocFailed ){ sqlite3ErrorMsg(&sParse, "out of memory"); @@ -139227,7 +140626,7 @@ static Select *findRightmost(Select *p){ ** NATURAL FULL OUTER JT_NATRUAL|JT_LEFT|JT_RIGHT ** ** To preserve historical compatibly, SQLite also accepts a variety -** of other non-standard and in many cases non-sensical join types. +** of other non-standard and in many cases nonsensical join types. ** This routine makes as much sense at it can from the nonsense join ** type and returns a result. Examples of accepted nonsense join types ** include but are not limited to: @@ -139498,7 +140897,7 @@ static int sqlite3ProcessJoin(Parse *pParse, Select *p){ if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue; joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_OuterON : EP_InnerON; - /* If this is a NATURAL join, synthesize an approprate USING clause + /* If this is a NATURAL join, synthesize an appropriate USING clause ** to specify which columns should be joined. */ if( pRight->fg.jointype & JT_NATURAL ){ @@ -139714,7 +141113,7 @@ static void pushOntoSorter( ** (3) Some output columns are omitted from the sort record due to ** the SQLITE_ENABLE_SORTER_REFERENCES optimization, or due to the ** SQLITE_ECEL_OMITREF optimization, or due to the - ** SortCtx.pDeferredRowLoad optimiation. In any of these cases + ** SortCtx.pDeferredRowLoad optimization. In any of these cases ** regOrigData is 0 to prevent this routine from trying to copy ** values that might not yet exist. */ @@ -139770,7 +141169,7 @@ static void pushOntoSorter( testcase( pKI->nAllField > pKI->nKeyField+2 ); pOp->p4.pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pSort->pOrderBy,nOBSat, pKI->nAllField-pKI->nKeyField-1); - pOp = 0; /* Ensure pOp not used after sqltie3VdbeAddOp3() */ + pOp = 0; /* Ensure pOp not used after sqlite3VdbeAddOp3() */ addrJmp = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); pSort->labelBkOut = sqlite3VdbeMakeLabel(pParse); @@ -139864,7 +141263,7 @@ static void codeOffset( ** The returned value in this case is a copy of parameter iTab. ** ** WHERE_DISTINCT_ORDERED: -** In this case rows are being delivered sorted order. The ephermal +** In this case rows are being delivered sorted order. The ephemeral ** table is not required. Instead, the current set of values ** is compared against previous row. If they match, the new row ** is not distinct and control jumps to VM address addrRepeat. Otherwise, @@ -140293,6 +141692,16 @@ static void selectInnerLoop( testcase( eDest==SRT_Fifo ); testcase( eDest==SRT_DistFifo ); sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1+nPrefixReg); +#if !defined(SQLITE_ENABLE_NULL_TRIM) && defined(SQLITE_DEBUG) + /* A destination of SRT_Table and a non-zero iSDParm2 parameter means + ** that this is an "UPDATE ... FROM" on a virtual table or view. In this + ** case set the p5 parameter of the OP_MakeRecord to OPFLAG_NOCHNG_MAGIC. + ** This does not affect operation in any way - it just allows MakeRecord + ** to process OPFLAG_NOCHANGE values without an assert() failing. */ + if( eDest==SRT_Table && pDest->iSDParm2 ){ + sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC); + } +#endif #ifndef SQLITE_OMIT_CTE if( eDest==SRT_DistFifo ){ /* If the destination is DistFifo, then cursor (iParm+1) is open @@ -141096,13 +142505,6 @@ SQLITE_PRIVATE void sqlite3GenerateColumnNames( int fullName; /* TABLE.COLUMN if no AS clause and is a direct table ref */ int srcName; /* COLUMN or TABLE.COLUMN if no AS clause and is direct */ -#ifndef SQLITE_OMIT_EXPLAIN - /* If this is an EXPLAIN, skip this step */ - if( pParse->explain ){ - return; - } -#endif - if( pParse->colNamesSet ) return; /* Column names are determined by the left-most term of a compound select */ while( pSelect->pPrior ) pSelect = pSelect->pPrior; @@ -141289,7 +142691,7 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList( ** kind (maybe a parenthesized subquery in the FROM clause of a larger ** query, or a VIEW, or a CTE). This routine computes type information ** for that Table object based on the Select object that implements the -** subquery. For the purposes of this routine, "type infomation" means: +** subquery. For the purposes of this routine, "type information" means: ** ** * The datatype name, as it might appear in a CREATE TABLE statement ** * Which collating sequence to use for the column @@ -141618,7 +143020,7 @@ static void generateWithRecursiveQuery( int iQueue; /* The Queue table */ int iDistinct = 0; /* To ensure unique results if UNION */ int eDest = SRT_Fifo; /* How to write to Queue */ - SelectDest destQueue; /* SelectDest targetting the Queue table */ + SelectDest destQueue; /* SelectDest targeting the Queue table */ int i; /* Loop counter */ int rc; /* Result code */ ExprList *pOrderBy; /* The ORDER BY clause */ @@ -142218,7 +143620,7 @@ SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p){ /* ** Code an output subroutine for a coroutine implementation of a -** SELECT statment. +** SELECT statement. ** ** The data to be output is contained in pIn->iSdst. There are ** pIn->nSdst columns to be output. pDest is where the output should @@ -142440,7 +143842,7 @@ static int generateOutputSubroutine( ** ** We call AltB, AeqB, AgtB, EofA, and EofB "subroutines" but they are not ** actually called using Gosub and they do not Return. EofA and EofB loop -** until all data is exhausted then jump to the "end" labe. AltB, AeqB, +** until all data is exhausted then jump to the "end" label. AltB, AeqB, ** and AgtB jump to either L2 or to one of EofA or EofB. */ #ifndef SQLITE_OMIT_COMPOUND_SELECT @@ -142477,7 +143879,7 @@ static int multiSelectOrderBy( int savedOffset; /* Saved value of p->iOffset */ int labelCmpr; /* Label for the start of the merge algorithm */ int labelEnd; /* Label for the end of the overall SELECT stmt */ - int addr1; /* Jump instructions that get retargetted */ + int addr1; /* Jump instructions that get retargeted */ int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */ KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ KeyInfo *pKeyMerge; /* Comparison information for merging rows */ @@ -142846,11 +144248,14 @@ static Expr *substExpr( #endif { Expr *pNew; - int iColumn = pExpr->iColumn; - Expr *pCopy = pSubst->pEList->a[iColumn].pExpr; + int iColumn; + Expr *pCopy; Expr ifNullRow; + iColumn = pExpr->iColumn; + assert( iColumn>=0 ); assert( pSubst->pEList!=0 && iColumnpEList->nExpr ); assert( pExpr->pRight==0 ); + pCopy = pSubst->pEList->a[iColumn].pExpr; if( sqlite3ExprIsVector(pCopy) ){ sqlite3VectorErrorMsg(pSubst->pParse, pCopy); }else{ @@ -143199,7 +144604,7 @@ static int compoundHasDifferentAffinities(Select *p){ ** (9) If the subquery uses LIMIT then the outer query may not be aggregate. ** ** (**) Restriction (10) was removed from the code on 2005-02-05 but we -** accidently carried the comment forward until 2014-09-15. Original +** accidentally carried the comment forward until 2014-09-15. Original ** constraint: "If the subquery is aggregate then the outer query ** may not use LIMIT." ** @@ -143291,7 +144696,8 @@ static int compoundHasDifferentAffinities(Select *p){ ** (27b) the subquery is a compound query and the RIGHT JOIN occurs ** in any arm of the compound query. (See also (17g).) ** -** (28) The subquery is not a MATERIALIZED CTE. +** (28) The subquery is not a MATERIALIZED CTE. (This is handled +** in the caller before ever reaching this routine.) ** ** ** In this routine, the "p" parameter is a pointer to the outer query. @@ -143401,9 +144807,9 @@ static int flattenSubquery( if( iFrom>0 && (pSubSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ return 0; /* Restriction (27a) */ } - if( pSubitem->fg.isCte && pSubitem->u2.pCteUse->eM10d==M10d_Yes ){ - return 0; /* (28) */ - } + + /* Condition (28) is blocked by the caller */ + assert( !pSubitem->fg.isCte || pSubitem->u2.pCteUse->eM10d!=M10d_Yes ); /* Restriction (17): If the sub-query is a compound SELECT, then it must ** use only the UNION ALL operator. And none of the simple select queries @@ -143473,7 +144879,7 @@ static int flattenSubquery( testcase( i==SQLITE_DENY ); pParse->zAuthContext = zSavedAuthContext; - /* Delete the transient structures associated with thesubquery */ + /* Delete the transient structures associated with the subquery */ pSub1 = pSubitem->pSelect; sqlite3DbFree(db, pSubitem->zDatabase); sqlite3DbFree(db, pSubitem->zName); @@ -143655,7 +145061,7 @@ static int flattenSubquery( ** ORDER BY column expression is identical to the iOrderByCol'th ** expression returned by SELECT statement pSub. Since these values ** do not necessarily correspond to columns in SELECT statement pParent, - ** zero them before transfering the ORDER BY clause. + ** zero them before transferring the ORDER BY clause. ** ** Not doing this may cause an error if a subsequent call to this ** function attempts to flatten a compound sub-query into pParent @@ -143715,8 +145121,7 @@ static int flattenSubquery( } } - /* Finially, delete what is left of the subquery and return - ** success. + /* Finally, delete what is left of the subquery and return success. */ sqlite3AggInfoPersistWalkerInit(&w, pParse); sqlite3WalkSelect(&w,pSub1); @@ -143751,7 +145156,7 @@ struct WhereConst { /* ** Add a new entry to the pConst object. Except, do not add duplicate -** pColumn entires. Also, do not add if doing so would not be appropriate. +** pColumn entries. Also, do not add if doing so would not be appropriate. ** ** The caller guarantees the pColumn is a column and pValue is a constant. ** This routine has to do some additional checks before completing the @@ -143937,7 +145342,7 @@ static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ ** SELECT * FROM t1 WHERE a=123 AND b=123; ** ** The two SELECT statements above should return different answers. b=a -** is alway true because the comparison uses numeric affinity, but b=123 +** is always true because the comparison uses numeric affinity, but b=123 ** is false because it uses text affinity and '0123' is not the same as '123'. ** To work around this, the expression tree is not actually changed from ** "b=a" to "b=123" but rather the "a" in "b=a" is tagged with EP_FixedCol @@ -144021,7 +145426,7 @@ static int propagateConstants( ** At the time this function is called it is guaranteed that ** ** * the sub-query uses only one distinct window frame, and -** * that the window frame has a PARTITION BY clase. +** * that the window frame has a PARTITION BY clause. */ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){ assert( pSubq->pWin->pPartition ); @@ -144290,12 +145695,12 @@ static int disableUnusedSubqueryResultColumns(SrcItem *pItem){ assert( pItem->pSelect!=0 ); pSub = pItem->pSelect; assert( pSub->pEList->nExpr==pTab->nCol ); - if( (pSub->selFlags & (SF_Distinct|SF_Aggregate))!=0 ){ - testcase( pSub->selFlags & SF_Distinct ); - testcase( pSub->selFlags & SF_Aggregate ); - return 0; - } for(pX=pSub; pX; pX=pX->pPrior){ + if( (pX->selFlags & (SF_Distinct|SF_Aggregate))!=0 ){ + testcase( pX->selFlags & SF_Distinct ); + testcase( pX->selFlags & SF_Aggregate ); + return 0; + } if( pX->pPrior && pX->op!=TK_ALL ){ /* This optimization does not work for compound subqueries that ** use UNION, INTERSECT, or EXCEPT. Only UNION ALL is allowed. */ @@ -145101,10 +146506,16 @@ static int selectExpander(Walker *pWalker, Select *p){ ** expanded. */ int tableSeen = 0; /* Set to 1 when TABLE matches */ char *zTName = 0; /* text of name of TABLE */ + int iErrOfst; if( pE->op==TK_DOT ){ assert( pE->pLeft!=0 ); assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); zTName = pE->pLeft->u.zToken; + assert( ExprUseWOfst(pE->pLeft) ); + iErrOfst = pE->pRight->w.iOfst; + }else{ + assert( ExprUseWOfst(pE) ); + iErrOfst = pE->w.iOfst; } for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ Table *pTab = pFrom->pTab; /* Table for this data source */ @@ -145141,6 +146552,7 @@ static int selectExpander(Walker *pWalker, Select *p){ for(ii=0; iinId; ii++){ const char *zUName = pUsing->a[ii].zName; pRight = sqlite3Expr(db, TK_ID, zUName); + sqlite3ExprSetErrorOffset(pRight, iErrOfst); pNew = sqlite3ExprListAppend(pParse, pNew, pRight); if( pNew ){ struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; @@ -145213,6 +146625,7 @@ static int selectExpander(Walker *pWalker, Select *p){ }else{ pExpr = pRight; } + sqlite3ExprSetErrorOffset(pExpr, iErrOfst); pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); if( pNew==0 ){ break; /* OOM */ @@ -145529,7 +146942,7 @@ static int aggregateIdxEprRefToColCallback(Walker *pWalker, Expr *pExpr){ pExpr->op = TK_AGG_COLUMN; pExpr->iTable = pCol->iTable; pExpr->iColumn = pCol->iColumn; - ExprClearProperty(pExpr, EP_Skip|EP_Collate); + ExprClearProperty(pExpr, EP_Skip|EP_Collate|EP_Unlikely); return WRC_Prune; } @@ -145560,7 +146973,7 @@ static void aggregateConvertIndexedExprRefToColumn(AggInfo *pAggInfo){ ** * The aCol[] and aFunc[] arrays may be modified ** * The AggInfoColumnReg() and AggInfoFuncReg() macros may not be used ** -** After clling this routine: +** After calling this routine: ** ** * The aCol[] and aFunc[] arrays are fixed ** * The AggInfoColumnReg() and AggInfoFuncReg() macros may be used @@ -146214,22 +147627,59 @@ SQLITE_PRIVATE int sqlite3Select( ** to a real table */ assert( pTab!=0 ); - /* Convert LEFT JOIN into JOIN if there are terms of the right table - ** of the LEFT JOIN used in the WHERE clause. + /* Try to simplify joins: + ** + ** LEFT JOIN -> JOIN + ** RIGHT JOIN -> JOIN + ** FULL JOIN -> RIGHT JOIN + ** + ** If terms of the i-th table are used in the WHERE clause in such a + ** way that the i-th table cannot be the NULL row of a join, then + ** perform the appropriate simplification. This is called + ** "OUTER JOIN strength reduction" in the SQLite documentation. */ - if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==JT_LEFT - && sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor) + if( (pItem->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 + && sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor, + pItem->fg.jointype & JT_LTORJ) && OptimizationEnabled(db, SQLITE_SimplifyJoin) ){ - TREETRACE(0x1000,pParse,p, - ("LEFT-JOIN simplifies to JOIN on term %d\n",i)); - pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER); + if( pItem->fg.jointype & JT_LEFT ){ + if( pItem->fg.jointype & JT_RIGHT ){ + TREETRACE(0x1000,pParse,p, + ("FULL-JOIN simplifies to RIGHT-JOIN on term %d\n",i)); + pItem->fg.jointype &= ~JT_LEFT; + }else{ + TREETRACE(0x1000,pParse,p, + ("LEFT-JOIN simplifies to JOIN on term %d\n",i)); + pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER); + } + } + if( pItem->fg.jointype & JT_LTORJ ){ + for(j=i+1; jnSrc; j++){ + SrcItem *pI2 = &pTabList->a[j]; + if( pI2->fg.jointype & JT_RIGHT ){ + if( pI2->fg.jointype & JT_LEFT ){ + TREETRACE(0x1000,pParse,p, + ("FULL-JOIN simplifies to LEFT-JOIN on term %d\n",j)); + pI2->fg.jointype &= ~JT_RIGHT; + }else{ + TREETRACE(0x1000,pParse,p, + ("RIGHT-JOIN simplifies to JOIN on term %d\n",j)); + pI2->fg.jointype &= ~(JT_RIGHT|JT_OUTER); + } + } + } + for(j=pTabList->nSrc-1; j>=i; j--){ + pTabList->a[j].fg.jointype &= ~JT_LTORJ; + if( pTabList->a[j].fg.jointype & JT_RIGHT ) break; + } + } assert( pItem->iCursor>=0 ); unsetJoinExpr(p->pWhere, pItem->iCursor, pTabList->a[0].fg.jointype & JT_LTORJ); } - /* No futher action if this term of the FROM clause is not a subquery */ + /* No further action if this term of the FROM clause is not a subquery */ if( pSub==0 ) continue; /* Catch mismatch in the declared columns of a view and the number of @@ -146240,6 +147690,14 @@ SQLITE_PRIVATE int sqlite3Select( goto select_end; } + /* Do not attempt the usual optimizations (flattening and ORDER BY + ** elimination) on a MATERIALIZED common table expression because + ** a MATERIALIZED common table expression is an optimization fence. + */ + if( pItem->fg.isCte && pItem->u2.pCteUse->eM10d==M10d_Yes ){ + continue; + } + /* Do not try to flatten an aggregate subquery. ** ** Flattening an aggregate subquery is only possible if the outer query @@ -146269,6 +147727,8 @@ SQLITE_PRIVATE int sqlite3Select( ** (a) The outer query has a different ORDER BY clause ** (b) The subquery is part of a join ** See forum post 062d576715d277c8 + ** + ** Also retain the ORDER BY if the OmitOrderBy optimization is disabled. */ if( pSub->pOrderBy!=0 && (p->pOrderBy!=0 || pTabList->nSrc>1) /* Condition (5) */ @@ -146483,7 +147943,7 @@ SQLITE_PRIVATE int sqlite3Select( }else if( pItem->fg.isCte && pItem->u2.pCteUse->addrM9e>0 ){ /* This is a CTE for which materialization code has already been ** generated. Invoke the subroutine to compute the materialization, - ** the make the pItem->iCursor be a copy of the ephemerial table that + ** the make the pItem->iCursor be a copy of the ephemeral table that ** holds the result of the materialization. */ CteUse *pCteUse = pItem->u2.pCteUse; sqlite3VdbeAddOp2(v, OP_Gosub, pCteUse->regRtn, pCteUse->addrM9e); @@ -146866,7 +148326,7 @@ SQLITE_PRIVATE int sqlite3Select( */ if( pGroupBy ){ KeyInfo *pKeyInfo; /* Keying information for the group by clause */ - int addr1; /* A-vs-B comparision jump */ + int addr1; /* A-vs-B comparison jump */ int addrOutputRow; /* Start of subroutine that outputs a result row */ int regOutputRow; /* Return address register for output subroutine */ int addrSetAbort; /* Set the abort flag and return */ @@ -146957,9 +148417,13 @@ SQLITE_PRIVATE int sqlite3Select( int nCol; int nGroupBy; - explainTempTable(pParse, +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + int addrExp; /* Address of OP_Explain instruction */ +#endif + ExplainQueryPlan2(addrExp, (pParse, 0, "USE TEMP B-TREE FOR %s", (sDistinct.isTnct && (p->selFlags&SF_Distinct)==0) ? - "DISTINCT" : "GROUP BY"); + "DISTINCT" : "GROUP BY" + )); groupBySort = 1; nGroupBy = pGroupBy->nExpr; @@ -146984,18 +148448,23 @@ SQLITE_PRIVATE int sqlite3Select( } pAggInfo->directMode = 0; regRecord = sqlite3GetTempReg(pParse); + sqlite3VdbeScanStatusCounters(v, addrExp, 0, sqlite3VdbeCurrentAddr(v)); sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord); sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord); + sqlite3VdbeScanStatusRange(v, addrExp, sqlite3VdbeCurrentAddr(v)-2, -1); sqlite3ReleaseTempReg(pParse, regRecord); sqlite3ReleaseTempRange(pParse, regBase, nCol); TREETRACE(0x2,pParse,p,("WhereEnd\n")); sqlite3WhereEnd(pWInfo); pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++; sortOut = sqlite3GetTempReg(pParse); + sqlite3VdbeScanStatusCounters(v, addrExp, sqlite3VdbeCurrentAddr(v), 0); sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol); sqlite3VdbeAddOp2(v, OP_SorterSort, pAggInfo->sortingIdx, addrEnd); VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v); pAggInfo->useSortingIdx = 1; + sqlite3VdbeScanStatusRange(v, addrExp, -1, sortPTab); + sqlite3VdbeScanStatusRange(v, addrExp, -1, pAggInfo->sortingIdx); } /* If there are entries in pAgggInfo->aFunc[] that contain subexpressions @@ -149246,7 +150715,7 @@ static void updateFromSelect( assert( pTabList->nSrc>1 ); if( pSrc ){ - pSrc->a[0].fg.notCte = 1; + assert( pSrc->a[0].fg.notCte ); pSrc->a[0].iCursor = -1; pSrc->a[0].pTab->nTabRef--; pSrc->a[0].pTab = 0; @@ -149763,7 +151232,7 @@ SQLITE_PRIVATE void sqlite3Update( && !hasFK && !chngKey && !bReplace - && (sNC.ncFlags & NC_Subquery)==0 + && (pWhere==0 || !ExprHasProperty(pWhere, EP_Subquery)) ){ flags |= WHERE_ONEPASS_MULTIROW; } @@ -149835,6 +151304,8 @@ SQLITE_PRIVATE void sqlite3Update( if( !isView ){ int addrOnce = 0; + int iNotUsed1 = 0; + int iNotUsed2 = 0; /* Open every index that needs updating. */ if( eOnePass!=ONEPASS_OFF ){ @@ -149846,7 +151317,7 @@ SQLITE_PRIVATE void sqlite3Update( addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); } sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, - aToOpen, 0, 0); + aToOpen, &iNotUsed1, &iNotUsed2); if( addrOnce ){ sqlite3VdbeJumpHereOrPopInst(v, addrOnce); } @@ -150137,8 +151608,10 @@ SQLITE_PRIVATE void sqlite3Update( sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); } - sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, - TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue); + if( pTrigger ){ + sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, + TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue); + } /* Repeat the above with the next record to be updated, until ** all record selected by the WHERE clause have been updated. @@ -150233,7 +151706,7 @@ static void updateVirtualTable( int nArg = 2 + pTab->nCol; /* Number of arguments to VUpdate */ int regArg; /* First register in VUpdate arg array */ int regRec; /* Register in which to assemble record */ - int regRowid; /* Register for ephem table rowid */ + int regRowid; /* Register for ephemeral table rowid */ int iCsr = pSrc->a[0].iCursor; /* Cursor used for virtual table scan */ int aDummy[2]; /* Unused arg for sqlite3WhereOkOnePass() */ int eOnePass; /* True to use onepass strategy */ @@ -150277,7 +151750,9 @@ static void updateVirtualTable( sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0) ); }else{ - pList = sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i)); + Expr *pRowExpr = exprRowColumn(pParse, i); + if( pRowExpr ) pRowExpr->op2 = OPFLAG_NOCHNG; + pList = sqlite3ExprListAppend(pParse, pList, pRowExpr); } } @@ -150354,7 +151829,7 @@ static void updateVirtualTable( sqlite3WhereEnd(pWInfo); } - /* Begin scannning through the ephemeral table. */ + /* Begin scanning through the ephemeral table. */ addr = sqlite3VdbeAddOp1(v, OP_Rewind, ephemTab); VdbeCoverage(v); /* Extract arguments from the current row of the ephemeral table and @@ -150562,7 +152037,7 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget( pExpr = &sCol[0]; } for(jj=0; jja[jj].pExpr,pExpr,iCursor)<2 ){ + if( sqlite3ExprCompare(0,pTarget->a[jj].pExpr,pExpr,iCursor)<2 ){ break; /* Column ii of the index matches column jj of target */ } } @@ -150911,7 +152386,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( ** (possibly synchronous) transaction opened on the main database before ** sqlite3BtreeCopyFile() is called. ** - ** An optimisation would be to use a non-journaled pager. + ** An optimization would be to use a non-journaled pager. ** (Later:) I tried setting "PRAGMA vacuum_db.journal_mode=OFF" but ** that actually made the VACUUM run slower. Very little journalling ** actually occurs when doing a vacuum since the vacuum_db is initially @@ -151600,7 +153075,7 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ ** the information we've collected. ** ** The VM register number pParse->regRowid holds the rowid of an - ** entry in the sqlite_schema table tht was created for this vtab + ** entry in the sqlite_schema table that was created for this vtab ** by sqlite3StartTable(). */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); @@ -152344,7 +153819,7 @@ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ ** ** An eponymous virtual table instance is one that is named after its ** module, and more importantly, does not require a CREATE VIRTUAL TABLE -** statement in order to come into existance. Eponymous virtual table +** statement in order to come into existence. Eponymous virtual table ** instances always exist. They cannot be DROP-ed. ** ** Any virtual table module for which xConnect and xCreate are the same @@ -152535,7 +154010,7 @@ typedef struct WhereRightJoin WhereRightJoin; /* ** This object is a header on a block of allocated memory that will be -** automatically freed when its WInfo oject is destructed. +** automatically freed when its WInfo object is destructed. */ struct WhereMemBlock { WhereMemBlock *pNext; /* Next block in the chain */ @@ -152596,7 +154071,7 @@ struct WhereLevel { int iCur; /* The VDBE cursor used by this IN operator */ int addrInTop; /* Top of the IN loop */ int iBase; /* Base register of multi-key index record */ - int nPrefix; /* Number of prior entires in the key */ + int nPrefix; /* Number of prior entries in the key */ u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */ } *aInLoop; /* Information about each nested IN operator */ } in; /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */ @@ -152846,7 +154321,7 @@ struct WhereClause { int nTerm; /* Number of terms */ int nSlot; /* Number of entries in a[] */ int nBase; /* Number of terms through the last non-Virtual */ - WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */ + WhereTerm *a; /* Each a[] describes a term of the WHERE clause */ #if defined(SQLITE_SMALL_STACK) WhereTerm aStatic[1]; /* Initial static space for a[] */ #else @@ -153434,6 +154909,12 @@ SQLITE_PRIVATE void sqlite3WhereAddScanStatus( if( wsFlags & WHERE_INDEXED ){ sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur); } + }else{ + int addr = pSrclist->a[pLvl->iFrom].addrFillSub; + VdbeOp *pOp = sqlite3VdbeGetOp(v, addr-1); + assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine ); + assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr ); + sqlite3VdbeScanStatusRange(v, addrExplain, addr, pOp->p2-1); } } } @@ -153931,7 +155412,7 @@ static int codeAllEqualityTerms( /* Figure out how many memory cells we will need then allocate them. */ regBase = pParse->nMem + 1; - nReg = pLoop->u.btree.nEq + nExtraReg; + nReg = nEq + nExtraReg; pParse->nMem += nReg; zAff = sqlite3DbStrDup(pParse->db,sqlite3IndexAffinityStr(pParse->db,pIdx)); @@ -153978,9 +155459,6 @@ static int codeAllEqualityTerms( sqlite3VdbeAddOp2(v, OP_Copy, r1, regBase+j); } } - } - for(j=nSkip; jaLTerm[j]; if( pTerm->eOperator & WO_IN ){ if( pTerm->pExpr->flags & EP_xIsSelect ){ /* No affinity ever needs to be (or should be) applied to a value @@ -154123,7 +155601,7 @@ static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){ ** 2) transform the expression node to a TK_REGISTER node that reads ** from the newly populated register. ** -** Also, if the node is a TK_COLUMN that does access the table idenified +** Also, if the node is a TK_COLUMN that does access the table identified ** by pCCurHint.iTabCur, and an index is being used (which we will ** know because CCurHint.pIdx!=0) then transform the TK_COLUMN into ** an access of the index rather than the original table. @@ -154741,7 +156219,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( }; assert( TK_LE==TK_GT+1 ); /* Make sure the ordering.. */ assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */ - assert( TK_GE==TK_GT+3 ); /* ... is correcct. */ + assert( TK_GE==TK_GT+3 ); /* ... is correct. */ assert( (pStart->wtFlags & TERM_VNULL)==0 ); testcase( pStart->wtFlags & TERM_VIRTUAL ); @@ -155921,7 +157399,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop( ** the WHERE clause of SQL statements. ** ** This file was originally part of where.c but was split out to improve -** readability and editabiliity. This file contains utility routines for +** readability and editability. This file contains utility routines for ** analyzing Expr objects in the WHERE clause. */ /* #include "sqliteInt.h" */ @@ -156137,7 +157615,7 @@ static int isLikeOrGlob( ** range search. The third is because the caller assumes that the pattern ** consists of at least one character after all escapes have been ** removed. */ - if( cnt!=0 && 255!=(u8)z[cnt-1] && (cnt>1 || z[0]!=wc[3]) ){ + if( (cnt>1 || (cnt>0 && z[0]!=wc[3])) && 255!=(u8)z[cnt-1] ){ Expr *pPrefix; /* A "complete" match if the pattern ends with "*" or "%" */ @@ -156710,7 +158188,7 @@ static void exprAnalyzeOrTerm( pOrTerm->leftCursor))==0 ){ /* This term must be of the form t1.a==t2.b where t2 is in the ** chngToIN set but t1 is not. This term will be either preceded - ** or follwed by an inverted copy (t2.b==t1.a). Skip this term + ** or followed by an inverted copy (t2.b==t1.a). Skip this term ** and use its inversion. */ testcase( pOrTerm->wtFlags & TERM_COPIED ); testcase( pOrTerm->wtFlags & TERM_VIRTUAL ); @@ -156972,8 +158450,8 @@ static void exprAnalyze( WhereTerm *pTerm; /* The term to be analyzed */ WhereMaskSet *pMaskSet; /* Set of table index masks */ Expr *pExpr; /* The expression to be analyzed */ - Bitmask prereqLeft; /* Prerequesites of the pExpr->pLeft */ - Bitmask prereqAll; /* Prerequesites of pExpr */ + Bitmask prereqLeft; /* Prerequisites of the pExpr->pLeft */ + Bitmask prereqAll; /* Prerequisites of pExpr */ Bitmask extraRight = 0; /* Extra dependencies on LEFT JOIN */ Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */ int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */ @@ -159534,7 +161012,7 @@ SQLITE_PRIVATE char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCo ** Value pLoop->nOut is currently set to the estimated number of rows ** visited for scanning (a=? AND b=?). This function reduces that estimate ** by some factor to account for the (c BETWEEN ? AND ?) expression based -** on the stat4 data for the index. this scan will be peformed multiple +** on the stat4 data for the index. this scan will be performed multiple ** times (once for each (a,b) combination that matches a=?) is dealt with ** by the caller. ** @@ -160289,7 +161767,7 @@ static WhereLoop **whereLoopFindLesser( ** rSetup. Call this SETUP-INVARIANT */ assert( p->rSetup>=pTemplate->rSetup ); - /* Any loop using an appliation-defined index (or PRIMARY KEY or + /* Any loop using an application-defined index (or PRIMARY KEY or ** UNIQUE constraint) with one or more == constraints is better ** than an automatic index. Unless it is a skip-scan. */ if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 @@ -160316,7 +161794,7 @@ static WhereLoop **whereLoopFindLesser( /* If pTemplate is always better than p, then cause p to be overwritten ** with pTemplate. pTemplate is better than p if: - ** (1) pTemplate has no more dependences than p, and + ** (1) pTemplate has no more dependencies than p, and ** (2) pTemplate has an equal or lower cost than p. */ if( (p->prereq & pTemplate->prereq)==pTemplate->prereq /* (1) */ @@ -160434,7 +161912,7 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ }else{ /* We will be overwriting WhereLoop p[]. But before we do, first ** go through the rest of the list and delete any other entries besides - ** p[] that are also supplated by pTemplate */ + ** p[] that are also supplanted by pTemplate */ WhereLoop **ppTail = &p->pNextLoop; WhereLoop *pToDel; while( *ppTail ){ @@ -160634,7 +162112,7 @@ static int whereRangeVectorLen( } /* -** Adjust the cost C by the costMult facter T. This only occurs if +** Adjust the cost C by the costMult factor T. This only occurs if ** compiled with -DSQLITE_ENABLE_COSTMULT */ #ifdef SQLITE_ENABLE_COSTMULT @@ -160661,7 +162139,7 @@ static int whereLoopAddBtreeIndex( Index *pProbe, /* An index on pSrc */ LogEst nInMul /* log(Number of iterations due to IN) */ ){ - WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyse context */ + WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyze context */ Parse *pParse = pWInfo->pParse; /* Parsing context */ sqlite3 *db = pParse->db; /* Database connection malloc context */ WhereLoop *pNew; /* Template WhereLoop under construction */ @@ -160971,7 +162449,7 @@ static int whereLoopAddBtreeIndex( assert( pSrc->pTab->szTabRow>0 ); if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ /* The pProbe->szIdxRow is low for an IPK table since the interior - ** pages are small. Thuse szIdxRow gives a good estimate of seek cost. + ** pages are small. Thus szIdxRow gives a good estimate of seek cost. ** But the leaf pages are full-size, so pProbe->szIdxRow would badly ** under-estimate the scanning cost. */ rCostIdx = pNew->nOut + 16; @@ -161316,7 +162794,7 @@ static SQLITE_NOINLINE u32 whereIsCoveringIndex( */ static int whereLoopAddBtree( WhereLoopBuilder *pBuilder, /* WHERE clause information */ - Bitmask mPrereq /* Extra prerequesites for using this table */ + Bitmask mPrereq /* Extra prerequisites for using this table */ ){ WhereInfo *pWInfo; /* WHERE analysis context */ Index *pProbe; /* An index we are evaluating */ @@ -161823,7 +163301,7 @@ static int whereLoopAddVirtualOne( ** ** Return a pointer to the collation name: ** -** 1. If there is an explicit COLLATE operator on the constaint, return it. +** 1. If there is an explicit COLLATE operator on the constraint, return it. ** ** 2. Else, if the column has an alternative collation, return that. ** @@ -162784,7 +164262,8 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ ** For joins of 3 or more tables, track the 10 best paths */ mxChoice = (nLoop<=1) ? 1 : (nLoop==2 ? 5 : 10); assert( nLoop<=pWInfo->pTabList->nSrc ); - WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d)\n", nRowEst)); + WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d, nQueryLoop=%d)\n", + nRowEst, pParse->nQueryLoop)); /* If nRowEst is zero and there is an ORDER BY clause, ignore it. In this ** case the purpose of this call is to estimate the number of rows returned @@ -162887,7 +164366,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ ); } /* TUNING: Add a small extra penalty (3) to sorting as an - ** extra encouragment to the query planner to select a plan + ** extra encouragement to the query planner to select a plan ** where the rows emerge in the correct order without any sorting ** required. */ rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]) + 3; @@ -162903,9 +164382,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ /* TUNING: A full-scan of a VIEW or subquery in the outer loop ** is not so bad. */ - if( iLoop==0 && (pWLoop->wsFlags & WHERE_VIEWSCAN)!=0 ){ + if( iLoop==0 && (pWLoop->wsFlags & WHERE_VIEWSCAN)!=0 && nLoop>1 ){ rCost += -10; nOut += -30; + WHERETRACE(0x80,("VIEWSCAN cost reduction for %c\n",pWLoop->cId)); } /* Check to see if pWLoop should be added to the set of @@ -163537,6 +165017,28 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( } } +/* +** Set the reverse-scan order mask to one for all tables in the query +** with the exception of MATERIALIZED common table expressions that have +** their own internal ORDER BY clauses. +** +** This implements the PRAGMA reverse_unordered_selects=ON setting. +** (Also SQLITE_DBCONFIG_REVERSE_SCANORDER). +*/ +static SQLITE_NOINLINE void whereReverseScanOrder(WhereInfo *pWInfo){ + int ii; + for(ii=0; iipTabList->nSrc; ii++){ + SrcItem *pItem = &pWInfo->pTabList->a[ii]; + if( !pItem->fg.isCte + || pItem->u2.pCteUse->eM10d!=M10d_Yes + || NEVER(pItem->pSelect==0) + || pItem->pSelect->pOrderBy==0 + ){ + pWInfo->revMask |= MASKBIT(ii); + } + } +} + /* ** Generate the beginning of the loop used for WHERE clause processing. ** The return value is a pointer to an opaque structure that contains @@ -163595,7 +165097,7 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( ** ** OUTER JOINS ** -** An outer join of tables t1 and t2 is conceptally coded as follows: +** An outer join of tables t1 and t2 is conceptually coded as follows: ** ** foreach row1 in t1 do ** flag = 0 @@ -163750,7 +165252,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( ** ** The N-th term of the FROM clause is assigned a bitmask of 1<mallocFailed ) goto whereBeginError; } } + assert( pWInfo->pTabList!=0 ); if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){ - pWInfo->revMask = ALLBITS; + whereReverseScanOrder(pWInfo); } if( pParse->nErr ){ goto whereBeginError; @@ -164002,6 +165505,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( 0!=(wctrlFlags & WHERE_ONEPASS_MULTIROW) && !IsVirtual(pTabList->a[0].pTab) && (0==(wsFlags & WHERE_MULTI_OR) || (wctrlFlags & WHERE_DUPLICATES_OK)) + && OptimizationEnabled(db, SQLITE_OnePass) )){ pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI; if( HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY) ){ @@ -164731,7 +166235,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ ** ** These are the same built-in window functions supported by Postgres. ** Although the behaviour of aggregate window functions (functions that -** can be used as either aggregates or window funtions) allows them to +** can be used as either aggregates or window functions) allows them to ** be implemented using an API, built-in window functions are much more ** esoteric. Additionally, some window functions (e.g. nth_value()) ** may only be implemented by caching the entire partition in memory. @@ -165261,7 +166765,7 @@ static Window *windowFind(Parse *pParse, Window *pList, const char *zName){ ** is the Window object representing the associated OVER clause. This ** function updates the contents of pWin as follows: ** -** * If the OVER clause refered to a named window (as in "max(x) OVER win"), +** * If the OVER clause referred to a named window (as in "max(x) OVER win"), ** search list pList for a matching WINDOW definition, and update pWin ** accordingly. If no such WINDOW clause can be found, leave an error ** in pParse. @@ -165882,7 +167386,7 @@ SQLITE_PRIVATE Window *sqlite3WindowAssemble( } /* -** Window *pWin has just been created from a WINDOW clause. Tokne pBase +** Window *pWin has just been created from a WINDOW clause. Token pBase ** is the base window. Earlier windows from the same WINDOW clause are ** stored in the linked list starting at pWin->pNextWin. This function ** either updates *pWin according to the base specification, or else @@ -166188,7 +167692,7 @@ struct WindowCsrAndReg { ** ** (ORDER BY a, b GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING) ** -** The windows functions implmentation caches the input rows in a temp +** The windows functions implementation caches the input rows in a temp ** table, sorted by "a, b" (it actually populates the cache lazily, and ** aggressively removes rows once they are no longer required, but that's ** a mere detail). It keeps three cursors open on the temp table. One @@ -167197,7 +168701,7 @@ static int windowExprGtZero(Parse *pParse, Expr *pExpr){ ** ** For the most part, the patterns above are adapted to support UNBOUNDED by ** assuming that it is equivalent to "infinity PRECEDING/FOLLOWING" and -** CURRENT ROW by assuming that it is equivilent to "0 PRECEDING/FOLLOWING". +** CURRENT ROW by assuming that it is equivalent to "0 PRECEDING/FOLLOWING". ** This is optimized of course - branches that will never be taken and ** conditions that are always true are omitted from the VM code. The only ** exceptional case is: @@ -167476,7 +168980,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( } /* Allocate registers for the array of values from the sub-query, the - ** samve values in record form, and the rowid used to insert said record + ** same values in record form, and the rowid used to insert said record ** into the ephemeral table. */ regNew = pParse->nMem+1; pParse->nMem += nInput; @@ -167717,7 +169221,8 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( /************** End of window.c **********************************************/ /************** Begin file parse.c *******************************************/ /* This file is automatically generated by Lemon from input grammar -** source file "parse.y". */ +** source file "parse.y". +*/ /* ** 2001-09-15 ** @@ -167734,7 +169239,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( ** The canonical source code to this file ("parse.y") is a Lemon grammar ** file that specifies the input grammar and actions to take while parsing. ** That input file is processed by Lemon to generate a C-language -** implementation of a parser for the given grammer. You might be reading +** implementation of a parser for the given grammar. You might be reading ** this comment as part of the translated C-code. Edits should be made ** to the original parse.y sources. */ @@ -168230,7 +169735,7 @@ typedef union { #define YYFALLBACK 1 #define YYNSTATE 575 #define YYNRULE 403 -#define YYNRULE_WITH_ACTION 340 +#define YYNRULE_WITH_ACTION 338 #define YYNTOKEN 185 #define YY_MAX_SHIFT 574 #define YY_MIN_SHIFTREDUCE 833 @@ -168312,106 +169817,106 @@ static const YYACTIONTYPE yy_action[] = { /* 10 */ 568, 1310, 377, 1289, 408, 562, 562, 562, 568, 409, /* 20 */ 378, 1310, 1272, 41, 41, 41, 41, 208, 1520, 71, /* 30 */ 71, 969, 419, 41, 41, 491, 303, 279, 303, 970, - /* 40 */ 397, 71, 71, 125, 126, 80, 1212, 1212, 1047, 1050, + /* 40 */ 397, 71, 71, 125, 126, 80, 1210, 1210, 1047, 1050, /* 50 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 476, 409, /* 60 */ 1237, 1, 1, 574, 2, 1241, 550, 118, 115, 229, /* 70 */ 317, 480, 146, 480, 524, 118, 115, 229, 529, 1323, - /* 80 */ 417, 523, 142, 125, 126, 80, 1212, 1212, 1047, 1050, + /* 80 */ 417, 523, 142, 125, 126, 80, 1210, 1210, 1047, 1050, /* 90 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 118, 115, /* 100 */ 229, 327, 122, 122, 122, 122, 121, 121, 120, 120, /* 110 */ 120, 119, 116, 444, 284, 284, 284, 284, 442, 442, - /* 120 */ 442, 1561, 376, 1563, 1188, 375, 1159, 565, 1159, 565, - /* 130 */ 409, 1561, 537, 259, 226, 444, 101, 145, 449, 316, + /* 120 */ 442, 1559, 376, 1561, 1186, 375, 1157, 565, 1157, 565, + /* 130 */ 409, 1559, 537, 259, 226, 444, 101, 145, 449, 316, /* 140 */ 559, 240, 122, 122, 122, 122, 121, 121, 120, 120, - /* 150 */ 120, 119, 116, 444, 125, 126, 80, 1212, 1212, 1047, + /* 150 */ 120, 119, 116, 444, 125, 126, 80, 1210, 1210, 1047, /* 160 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 142, - /* 170 */ 294, 1188, 339, 448, 120, 120, 120, 119, 116, 444, - /* 180 */ 127, 1188, 1189, 1188, 148, 441, 440, 568, 119, 116, + /* 170 */ 294, 1186, 339, 448, 120, 120, 120, 119, 116, 444, + /* 180 */ 127, 1186, 1187, 1186, 148, 441, 440, 568, 119, 116, /* 190 */ 444, 124, 124, 124, 124, 117, 122, 122, 122, 122, /* 200 */ 121, 121, 120, 120, 120, 119, 116, 444, 454, 113, /* 210 */ 13, 13, 546, 122, 122, 122, 122, 121, 121, 120, - /* 220 */ 120, 120, 119, 116, 444, 422, 316, 559, 1188, 1189, - /* 230 */ 1188, 149, 1220, 409, 1220, 124, 124, 124, 124, 122, + /* 220 */ 120, 120, 119, 116, 444, 422, 316, 559, 1186, 1187, + /* 230 */ 1186, 149, 1218, 409, 1218, 124, 124, 124, 124, 122, /* 240 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, /* 250 */ 444, 465, 342, 1034, 1034, 1048, 1051, 125, 126, 80, - /* 260 */ 1212, 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, - /* 270 */ 124, 124, 1275, 522, 222, 1188, 568, 409, 224, 514, + /* 260 */ 1210, 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, + /* 270 */ 124, 124, 1275, 522, 222, 1186, 568, 409, 224, 514, /* 280 */ 175, 82, 83, 122, 122, 122, 122, 121, 121, 120, - /* 290 */ 120, 120, 119, 116, 444, 1005, 16, 16, 1188, 133, - /* 300 */ 133, 125, 126, 80, 1212, 1212, 1047, 1050, 1037, 1037, + /* 290 */ 120, 120, 119, 116, 444, 1005, 16, 16, 1186, 133, + /* 300 */ 133, 125, 126, 80, 1210, 1210, 1047, 1050, 1037, 1037, /* 310 */ 123, 123, 124, 124, 124, 124, 122, 122, 122, 122, /* 320 */ 121, 121, 120, 120, 120, 119, 116, 444, 1038, 546, - /* 330 */ 1188, 373, 1188, 1189, 1188, 252, 1429, 399, 504, 501, + /* 330 */ 1186, 373, 1186, 1187, 1186, 252, 1429, 399, 504, 501, /* 340 */ 500, 111, 560, 566, 4, 924, 924, 433, 499, 340, - /* 350 */ 460, 328, 360, 394, 1233, 1188, 1189, 1188, 563, 568, + /* 350 */ 460, 328, 360, 394, 1231, 1186, 1187, 1186, 563, 568, /* 360 */ 122, 122, 122, 122, 121, 121, 120, 120, 120, 119, - /* 370 */ 116, 444, 284, 284, 369, 1574, 1600, 441, 440, 154, - /* 380 */ 409, 445, 71, 71, 1282, 565, 1217, 1188, 1189, 1188, - /* 390 */ 85, 1219, 271, 557, 543, 515, 1555, 568, 98, 1218, - /* 400 */ 6, 1274, 472, 142, 125, 126, 80, 1212, 1212, 1047, + /* 370 */ 116, 444, 284, 284, 369, 1572, 1598, 441, 440, 154, + /* 380 */ 409, 445, 71, 71, 1282, 565, 1215, 1186, 1187, 1186, + /* 390 */ 85, 1217, 271, 557, 543, 515, 515, 568, 98, 1216, + /* 400 */ 6, 1274, 472, 142, 125, 126, 80, 1210, 1210, 1047, /* 410 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 550, - /* 420 */ 13, 13, 1024, 507, 1220, 1188, 1220, 549, 109, 109, - /* 430 */ 222, 568, 1234, 175, 568, 427, 110, 197, 445, 569, - /* 440 */ 445, 430, 1546, 1014, 325, 551, 1188, 270, 287, 368, + /* 420 */ 13, 13, 1024, 507, 1218, 1186, 1218, 549, 109, 109, + /* 430 */ 222, 568, 1232, 175, 568, 427, 110, 197, 445, 569, + /* 440 */ 445, 430, 1546, 1014, 325, 551, 1186, 270, 287, 368, /* 450 */ 510, 363, 509, 257, 71, 71, 543, 71, 71, 359, - /* 460 */ 316, 559, 1606, 122, 122, 122, 122, 121, 121, 120, + /* 460 */ 316, 559, 1604, 122, 122, 122, 122, 121, 121, 120, /* 470 */ 120, 120, 119, 116, 444, 1014, 1014, 1016, 1017, 27, - /* 480 */ 284, 284, 1188, 1189, 1188, 1154, 568, 1605, 409, 899, - /* 490 */ 190, 550, 356, 565, 550, 935, 533, 517, 1154, 516, - /* 500 */ 413, 1154, 552, 1188, 1189, 1188, 568, 544, 1548, 51, - /* 510 */ 51, 214, 125, 126, 80, 1212, 1212, 1047, 1050, 1037, - /* 520 */ 1037, 123, 123, 124, 124, 124, 124, 1188, 474, 135, + /* 480 */ 284, 284, 1186, 1187, 1186, 1152, 568, 1603, 409, 899, + /* 490 */ 190, 550, 356, 565, 550, 935, 533, 517, 1152, 516, + /* 500 */ 413, 1152, 552, 1186, 1187, 1186, 568, 544, 544, 51, + /* 510 */ 51, 214, 125, 126, 80, 1210, 1210, 1047, 1050, 1037, + /* 520 */ 1037, 123, 123, 124, 124, 124, 124, 1186, 474, 135, /* 530 */ 135, 409, 284, 284, 1484, 505, 121, 121, 120, 120, - /* 540 */ 120, 119, 116, 444, 1005, 565, 518, 217, 541, 1555, - /* 550 */ 316, 559, 142, 6, 532, 125, 126, 80, 1212, 1212, + /* 540 */ 120, 119, 116, 444, 1005, 565, 518, 217, 541, 541, + /* 550 */ 316, 559, 142, 6, 532, 125, 126, 80, 1210, 1210, /* 560 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, - /* 570 */ 1549, 122, 122, 122, 122, 121, 121, 120, 120, 120, - /* 580 */ 119, 116, 444, 485, 1188, 1189, 1188, 482, 281, 1263, - /* 590 */ 955, 252, 1188, 373, 504, 501, 500, 1188, 340, 570, - /* 600 */ 1188, 570, 409, 292, 499, 955, 874, 191, 480, 316, + /* 570 */ 1548, 122, 122, 122, 122, 121, 121, 120, 120, 120, + /* 580 */ 119, 116, 444, 485, 1186, 1187, 1186, 482, 281, 1263, + /* 590 */ 955, 252, 1186, 373, 504, 501, 500, 1186, 340, 570, + /* 600 */ 1186, 570, 409, 292, 499, 955, 874, 191, 480, 316, /* 610 */ 559, 384, 290, 380, 122, 122, 122, 122, 121, 121, - /* 620 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, - /* 630 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, - /* 640 */ 124, 409, 394, 1132, 1188, 867, 100, 284, 284, 1188, - /* 650 */ 1189, 1188, 373, 1089, 1188, 1189, 1188, 1188, 1189, 1188, - /* 660 */ 565, 455, 32, 373, 233, 125, 126, 80, 1212, 1212, + /* 620 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210, + /* 630 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 640 */ 124, 409, 394, 1132, 1186, 867, 100, 284, 284, 1186, + /* 650 */ 1187, 1186, 373, 1089, 1186, 1187, 1186, 1186, 1187, 1186, + /* 660 */ 565, 455, 32, 373, 233, 125, 126, 80, 1210, 1210, /* 670 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, /* 680 */ 1428, 957, 568, 228, 956, 122, 122, 122, 122, 121, - /* 690 */ 121, 120, 120, 120, 119, 116, 444, 1154, 228, 1188, - /* 700 */ 157, 1188, 1189, 1188, 1547, 13, 13, 301, 955, 1228, - /* 710 */ 1154, 153, 409, 1154, 373, 1577, 1172, 5, 369, 1574, - /* 720 */ 429, 1234, 3, 955, 122, 122, 122, 122, 121, 121, - /* 730 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, - /* 740 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, - /* 750 */ 124, 409, 208, 567, 1188, 1025, 1188, 1189, 1188, 1188, + /* 690 */ 121, 120, 120, 120, 119, 116, 444, 1152, 228, 1186, + /* 700 */ 157, 1186, 1187, 1186, 1547, 13, 13, 301, 955, 1226, + /* 710 */ 1152, 153, 409, 1152, 373, 1575, 1170, 5, 369, 1572, + /* 720 */ 429, 1232, 3, 955, 122, 122, 122, 122, 121, 121, + /* 730 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210, + /* 740 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 750 */ 124, 409, 208, 567, 1186, 1025, 1186, 1187, 1186, 1186, /* 760 */ 388, 850, 155, 1546, 286, 402, 1094, 1094, 488, 568, - /* 770 */ 465, 342, 1315, 1315, 1546, 125, 126, 80, 1212, 1212, + /* 770 */ 465, 342, 1315, 1315, 1546, 125, 126, 80, 1210, 1210, /* 780 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, /* 790 */ 129, 568, 13, 13, 374, 122, 122, 122, 122, 121, /* 800 */ 121, 120, 120, 120, 119, 116, 444, 302, 568, 453, - /* 810 */ 528, 1188, 1189, 1188, 13, 13, 1188, 1189, 1188, 1293, + /* 810 */ 528, 1186, 1187, 1186, 13, 13, 1186, 1187, 1186, 1293, /* 820 */ 463, 1263, 409, 1313, 1313, 1546, 1010, 453, 452, 200, /* 830 */ 299, 71, 71, 1261, 122, 122, 122, 122, 121, 121, - /* 840 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, - /* 850 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, - /* 860 */ 124, 409, 227, 1069, 1154, 284, 284, 419, 312, 278, - /* 870 */ 278, 285, 285, 1415, 406, 405, 382, 1154, 565, 568, - /* 880 */ 1154, 1191, 565, 1594, 565, 125, 126, 80, 1212, 1212, + /* 840 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210, + /* 850 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 860 */ 124, 409, 227, 1069, 1152, 284, 284, 419, 312, 278, + /* 870 */ 278, 285, 285, 1415, 406, 405, 382, 1152, 565, 568, + /* 880 */ 1152, 1189, 565, 1592, 565, 125, 126, 80, 1210, 1210, /* 890 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, /* 900 */ 453, 1476, 13, 13, 1530, 122, 122, 122, 122, 121, /* 910 */ 121, 120, 120, 120, 119, 116, 444, 201, 568, 354, - /* 920 */ 1580, 574, 2, 1241, 838, 839, 840, 1556, 317, 1207, - /* 930 */ 146, 6, 409, 255, 254, 253, 206, 1323, 9, 1191, + /* 920 */ 1578, 574, 2, 1241, 838, 839, 840, 1554, 317, 1205, + /* 930 */ 146, 6, 409, 255, 254, 253, 206, 1323, 9, 1189, /* 940 */ 262, 71, 71, 424, 122, 122, 122, 122, 121, 121, - /* 950 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, - /* 960 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, - /* 970 */ 124, 568, 284, 284, 568, 1208, 409, 573, 313, 1241, - /* 980 */ 349, 1292, 352, 419, 317, 565, 146, 491, 525, 1637, + /* 950 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210, + /* 960 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 970 */ 124, 568, 284, 284, 568, 1206, 409, 573, 313, 1241, + /* 980 */ 349, 1292, 352, 419, 317, 565, 146, 491, 525, 1635, /* 990 */ 395, 371, 491, 1323, 70, 70, 1291, 71, 71, 240, - /* 1000 */ 1321, 104, 80, 1212, 1212, 1047, 1050, 1037, 1037, 123, + /* 1000 */ 1321, 104, 80, 1210, 1210, 1047, 1050, 1037, 1037, 123, /* 1010 */ 123, 124, 124, 124, 124, 122, 122, 122, 122, 121, /* 1020 */ 121, 120, 120, 120, 119, 116, 444, 1110, 284, 284, - /* 1030 */ 428, 448, 1519, 1208, 439, 284, 284, 1483, 1348, 311, + /* 1030 */ 428, 448, 1519, 1206, 439, 284, 284, 1483, 1348, 311, /* 1040 */ 474, 565, 1111, 969, 491, 491, 217, 1259, 565, 1532, /* 1050 */ 568, 970, 207, 568, 1024, 240, 383, 1112, 519, 122, /* 1060 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, @@ -168419,29 +169924,29 @@ static const YYACTIONTYPE yy_action[] = { /* 1080 */ 1489, 568, 284, 284, 97, 526, 491, 448, 911, 1322, /* 1090 */ 1318, 545, 409, 284, 284, 565, 151, 209, 1489, 1491, /* 1100 */ 262, 450, 55, 55, 56, 56, 565, 1014, 1014, 1016, - /* 1110 */ 443, 332, 409, 527, 12, 295, 125, 126, 80, 1212, - /* 1120 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, - /* 1130 */ 124, 347, 409, 862, 1528, 1208, 125, 126, 80, 1212, - /* 1140 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, - /* 1150 */ 124, 1133, 1635, 474, 1635, 371, 125, 114, 80, 1212, - /* 1160 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 1110 */ 443, 332, 409, 527, 12, 295, 125, 126, 80, 1210, + /* 1120 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 1130 */ 124, 347, 409, 862, 1528, 1206, 125, 126, 80, 1210, + /* 1140 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 1150 */ 124, 1133, 1633, 474, 1633, 371, 125, 114, 80, 1210, + /* 1160 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, /* 1170 */ 124, 1489, 329, 474, 331, 122, 122, 122, 122, 121, /* 1180 */ 121, 120, 120, 120, 119, 116, 444, 203, 1415, 568, - /* 1190 */ 1290, 862, 464, 1208, 436, 122, 122, 122, 122, 121, - /* 1200 */ 121, 120, 120, 120, 119, 116, 444, 553, 1133, 1636, - /* 1210 */ 539, 1636, 15, 15, 890, 122, 122, 122, 122, 121, + /* 1190 */ 1290, 862, 464, 1206, 436, 122, 122, 122, 122, 121, + /* 1200 */ 121, 120, 120, 120, 119, 116, 444, 553, 1133, 1634, + /* 1210 */ 539, 1634, 15, 15, 890, 122, 122, 122, 122, 121, /* 1220 */ 121, 120, 120, 120, 119, 116, 444, 568, 298, 538, - /* 1230 */ 1131, 1415, 1553, 1554, 1327, 409, 6, 6, 1165, 1264, + /* 1230 */ 1131, 1415, 1552, 1553, 1327, 409, 6, 6, 1163, 1264, /* 1240 */ 415, 320, 284, 284, 1415, 508, 565, 525, 300, 457, /* 1250 */ 43, 43, 568, 891, 12, 565, 330, 478, 425, 407, - /* 1260 */ 126, 80, 1212, 1212, 1047, 1050, 1037, 1037, 123, 123, - /* 1270 */ 124, 124, 124, 124, 568, 57, 57, 288, 1188, 1415, - /* 1280 */ 496, 458, 392, 392, 391, 273, 389, 1131, 1552, 847, - /* 1290 */ 1165, 407, 6, 568, 321, 1154, 470, 44, 44, 1551, - /* 1300 */ 1110, 426, 234, 6, 323, 256, 540, 256, 1154, 431, - /* 1310 */ 568, 1154, 322, 17, 487, 1111, 58, 58, 122, 122, + /* 1260 */ 126, 80, 1210, 1210, 1047, 1050, 1037, 1037, 123, 123, + /* 1270 */ 124, 124, 124, 124, 568, 57, 57, 288, 1186, 1415, + /* 1280 */ 496, 458, 392, 392, 391, 273, 389, 1131, 1551, 847, + /* 1290 */ 1163, 407, 6, 568, 321, 1152, 470, 44, 44, 1550, + /* 1300 */ 1110, 426, 234, 6, 323, 256, 540, 256, 1152, 431, + /* 1310 */ 568, 1152, 322, 17, 487, 1111, 58, 58, 122, 122, /* 1320 */ 122, 122, 121, 121, 120, 120, 120, 119, 116, 444, - /* 1330 */ 1112, 216, 481, 59, 59, 1188, 1189, 1188, 111, 560, + /* 1330 */ 1112, 216, 481, 59, 59, 1186, 1187, 1186, 111, 560, /* 1340 */ 324, 4, 236, 456, 526, 568, 237, 456, 568, 437, /* 1350 */ 168, 556, 420, 141, 479, 563, 568, 293, 568, 1091, /* 1360 */ 568, 293, 568, 1091, 531, 568, 870, 8, 60, 60, @@ -168455,7 +169960,7 @@ static const YYACTIONTYPE yy_action[] = { /* 1440 */ 1014, 132, 132, 67, 67, 568, 467, 568, 930, 471, /* 1450 */ 1360, 283, 226, 929, 315, 1359, 407, 568, 459, 407, /* 1460 */ 1014, 1014, 1016, 239, 407, 86, 213, 1346, 52, 52, - /* 1470 */ 68, 68, 1014, 1014, 1016, 1017, 27, 1579, 1176, 447, + /* 1470 */ 68, 68, 1014, 1014, 1016, 1017, 27, 1577, 1174, 447, /* 1480 */ 69, 69, 288, 97, 108, 1535, 106, 392, 392, 391, /* 1490 */ 273, 389, 568, 877, 847, 881, 568, 111, 560, 466, /* 1500 */ 4, 568, 152, 30, 38, 568, 1128, 234, 396, 323, @@ -168464,7 +169969,7 @@ static const YYACTIONTYPE yy_action[] = { /* 1530 */ 568, 289, 1508, 568, 31, 1507, 568, 445, 338, 483, /* 1540 */ 100, 54, 54, 344, 72, 72, 296, 236, 1076, 557, /* 1550 */ 445, 877, 1356, 134, 134, 168, 73, 73, 141, 161, - /* 1560 */ 161, 1568, 557, 535, 568, 319, 568, 348, 536, 1007, + /* 1560 */ 161, 1566, 557, 535, 568, 319, 568, 348, 536, 1007, /* 1570 */ 473, 261, 261, 889, 888, 235, 535, 568, 1024, 568, /* 1580 */ 475, 534, 261, 367, 109, 109, 521, 136, 136, 130, /* 1590 */ 130, 1024, 110, 366, 445, 569, 445, 109, 109, 1014, @@ -168472,7 +169977,7 @@ static const YYACTIONTYPE yy_action[] = { /* 1610 */ 410, 351, 1014, 568, 353, 316, 559, 568, 343, 568, /* 1620 */ 100, 497, 357, 258, 100, 896, 897, 140, 140, 355, /* 1630 */ 1306, 1014, 1014, 1016, 1017, 27, 139, 139, 362, 451, - /* 1640 */ 137, 137, 138, 138, 1014, 1014, 1016, 1017, 27, 1176, + /* 1640 */ 137, 137, 138, 138, 1014, 1014, 1016, 1017, 27, 1174, /* 1650 */ 447, 568, 372, 288, 111, 560, 1018, 4, 392, 392, /* 1660 */ 391, 273, 389, 568, 1137, 847, 568, 1072, 568, 258, /* 1670 */ 492, 563, 568, 211, 75, 75, 555, 960, 234, 261, @@ -168480,44 +169985,44 @@ static const YYACTIONTYPE yy_action[] = { /* 1690 */ 74, 42, 42, 1369, 445, 48, 48, 1414, 563, 972, /* 1700 */ 973, 1088, 1087, 1088, 1087, 860, 557, 150, 928, 1342, /* 1710 */ 113, 1354, 554, 1419, 1018, 1271, 1262, 1250, 236, 1249, - /* 1720 */ 1251, 445, 1587, 1339, 308, 276, 168, 309, 11, 141, + /* 1720 */ 1251, 445, 1585, 1339, 308, 276, 168, 309, 11, 141, /* 1730 */ 393, 310, 232, 557, 1401, 1024, 335, 291, 1396, 219, /* 1740 */ 336, 109, 109, 934, 297, 1406, 235, 341, 477, 110, /* 1750 */ 502, 445, 569, 445, 1389, 1405, 1014, 400, 1289, 365, /* 1760 */ 223, 1480, 1024, 1479, 1351, 1352, 1350, 1349, 109, 109, - /* 1770 */ 204, 1590, 1228, 558, 265, 218, 110, 205, 445, 569, + /* 1770 */ 204, 1588, 1226, 558, 265, 218, 110, 205, 445, 569, /* 1780 */ 445, 410, 387, 1014, 1527, 179, 316, 559, 1014, 1014, - /* 1790 */ 1016, 1017, 27, 230, 1525, 1225, 79, 560, 85, 4, + /* 1790 */ 1016, 1017, 27, 230, 1525, 1223, 79, 560, 85, 4, /* 1800 */ 418, 215, 548, 81, 84, 188, 1402, 173, 181, 461, /* 1810 */ 451, 35, 462, 563, 183, 1014, 1014, 1016, 1017, 27, /* 1820 */ 184, 1485, 185, 186, 495, 242, 98, 398, 1408, 36, /* 1830 */ 1407, 484, 91, 469, 401, 1410, 445, 192, 1474, 246, /* 1840 */ 1496, 490, 346, 277, 248, 196, 493, 511, 557, 350, /* 1850 */ 1252, 249, 250, 403, 1309, 1308, 111, 560, 432, 4, - /* 1860 */ 1307, 1300, 93, 1604, 881, 1603, 224, 404, 434, 520, - /* 1870 */ 263, 435, 1573, 563, 1279, 1278, 364, 1024, 306, 1277, - /* 1880 */ 264, 1602, 1559, 109, 109, 370, 1299, 307, 1558, 438, + /* 1860 */ 1307, 1300, 93, 1602, 881, 1601, 224, 404, 434, 520, + /* 1870 */ 263, 435, 1571, 563, 1279, 1278, 364, 1024, 306, 1277, + /* 1880 */ 264, 1600, 1557, 109, 109, 370, 1299, 307, 1556, 438, /* 1890 */ 128, 110, 1374, 445, 569, 445, 445, 546, 1014, 10, /* 1900 */ 1461, 105, 381, 1373, 34, 571, 99, 1332, 557, 314, - /* 1910 */ 1182, 530, 272, 274, 379, 210, 1331, 547, 385, 386, + /* 1910 */ 1180, 530, 272, 274, 379, 210, 1331, 547, 385, 386, /* 1920 */ 275, 572, 1247, 1242, 411, 412, 1512, 165, 178, 1513, /* 1930 */ 1014, 1014, 1016, 1017, 27, 1511, 1510, 1024, 78, 147, /* 1940 */ 166, 220, 221, 109, 109, 834, 304, 167, 446, 212, /* 1950 */ 318, 110, 231, 445, 569, 445, 144, 1086, 1014, 1084, - /* 1960 */ 326, 180, 169, 1207, 182, 334, 238, 913, 241, 1100, + /* 1960 */ 326, 180, 169, 1205, 182, 334, 238, 913, 241, 1100, /* 1970 */ 187, 170, 171, 421, 87, 88, 423, 189, 89, 90, /* 1980 */ 172, 1103, 243, 1099, 244, 158, 18, 245, 345, 247, - /* 1990 */ 1014, 1014, 1016, 1017, 27, 261, 1092, 193, 1222, 489, + /* 1990 */ 1014, 1014, 1016, 1017, 27, 261, 1092, 193, 1220, 489, /* 2000 */ 194, 37, 366, 849, 494, 251, 195, 506, 92, 19, /* 2010 */ 498, 358, 20, 503, 879, 361, 94, 892, 305, 159, - /* 2020 */ 513, 39, 95, 1170, 160, 1053, 964, 1139, 96, 174, - /* 2030 */ 1138, 225, 280, 282, 198, 958, 113, 1160, 1156, 260, - /* 2040 */ 21, 22, 23, 1158, 1164, 1163, 1144, 24, 33, 25, + /* 2020 */ 513, 39, 95, 1168, 160, 1053, 964, 1139, 96, 174, + /* 2030 */ 1138, 225, 280, 282, 198, 958, 113, 1158, 1154, 260, + /* 2040 */ 21, 22, 23, 1156, 1162, 1161, 1143, 24, 33, 25, /* 2050 */ 202, 542, 26, 100, 1067, 102, 1054, 103, 7, 1052, /* 2060 */ 1056, 1109, 1057, 1108, 266, 267, 28, 40, 390, 1019, - /* 2070 */ 861, 112, 29, 564, 1178, 1177, 268, 176, 143, 923, + /* 2070 */ 861, 112, 29, 564, 1176, 1175, 268, 176, 143, 923, /* 2080 */ 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, - /* 2090 */ 1238, 1238, 1238, 1238, 269, 1595, + /* 2090 */ 1238, 1238, 1238, 1238, 269, 1593, }; static const YYCODETYPE yy_lookahead[] = { /* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276, @@ -168860,14 +170365,14 @@ static const short yy_reduce_ofst[] = { /* 400 */ 1722, 1723, 1733, 1717, 1724, 1727, 1728, 1725, 1740, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 1641, 1641, 1641, 1469, 1236, 1347, 1236, 1236, 1236, 1469, + /* 0 */ 1639, 1639, 1639, 1469, 1236, 1347, 1236, 1236, 1236, 1469, /* 10 */ 1469, 1469, 1236, 1377, 1377, 1522, 1269, 1236, 1236, 1236, /* 20 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1468, 1236, 1236, - /* 30 */ 1236, 1236, 1557, 1557, 1236, 1236, 1236, 1236, 1236, 1236, + /* 30 */ 1236, 1236, 1555, 1555, 1236, 1236, 1236, 1236, 1236, 1236, /* 40 */ 1236, 1236, 1386, 1236, 1393, 1236, 1236, 1236, 1236, 1236, /* 50 */ 1470, 1471, 1236, 1236, 1236, 1521, 1523, 1486, 1400, 1399, /* 60 */ 1398, 1397, 1504, 1365, 1391, 1384, 1388, 1465, 1466, 1464, - /* 70 */ 1619, 1471, 1470, 1236, 1387, 1433, 1449, 1432, 1236, 1236, + /* 70 */ 1617, 1471, 1470, 1236, 1387, 1433, 1449, 1432, 1236, 1236, /* 80 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, /* 90 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, /* 100 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, @@ -168876,47 +170381,47 @@ static const YYACTIONTYPE yy_default[] = { /* 130 */ 1441, 1448, 1447, 1446, 1455, 1445, 1442, 1435, 1434, 1436, /* 140 */ 1437, 1236, 1236, 1260, 1236, 1236, 1257, 1311, 1236, 1236, /* 150 */ 1236, 1236, 1236, 1541, 1540, 1236, 1438, 1236, 1269, 1427, - /* 160 */ 1426, 1452, 1439, 1451, 1450, 1529, 1593, 1592, 1487, 1236, - /* 170 */ 1236, 1236, 1236, 1236, 1236, 1557, 1236, 1236, 1236, 1236, + /* 160 */ 1426, 1452, 1439, 1451, 1450, 1529, 1591, 1590, 1487, 1236, + /* 170 */ 1236, 1236, 1236, 1236, 1236, 1555, 1236, 1236, 1236, 1236, /* 180 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, /* 190 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1367, - /* 200 */ 1557, 1557, 1236, 1269, 1557, 1557, 1368, 1368, 1265, 1265, + /* 200 */ 1555, 1555, 1236, 1269, 1555, 1555, 1368, 1368, 1265, 1265, /* 210 */ 1371, 1236, 1536, 1338, 1338, 1338, 1338, 1347, 1338, 1236, /* 220 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, /* 230 */ 1236, 1236, 1236, 1236, 1526, 1524, 1236, 1236, 1236, 1236, /* 240 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, /* 250 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, /* 260 */ 1236, 1236, 1236, 1343, 1236, 1236, 1236, 1236, 1236, 1236, - /* 270 */ 1236, 1236, 1236, 1236, 1236, 1586, 1236, 1499, 1325, 1343, - /* 280 */ 1343, 1343, 1343, 1345, 1326, 1324, 1337, 1270, 1243, 1633, - /* 290 */ 1403, 1392, 1344, 1392, 1630, 1390, 1403, 1403, 1390, 1403, - /* 300 */ 1344, 1630, 1286, 1608, 1281, 1377, 1377, 1377, 1367, 1367, - /* 310 */ 1367, 1367, 1371, 1371, 1467, 1344, 1337, 1236, 1633, 1633, - /* 320 */ 1353, 1353, 1632, 1632, 1353, 1487, 1616, 1412, 1314, 1320, - /* 330 */ 1320, 1320, 1320, 1353, 1254, 1390, 1616, 1616, 1390, 1412, - /* 340 */ 1314, 1390, 1314, 1390, 1353, 1254, 1503, 1627, 1353, 1254, + /* 270 */ 1236, 1236, 1236, 1236, 1236, 1584, 1236, 1499, 1325, 1343, + /* 280 */ 1343, 1343, 1343, 1345, 1326, 1324, 1337, 1270, 1243, 1631, + /* 290 */ 1403, 1392, 1344, 1392, 1628, 1390, 1403, 1403, 1390, 1403, + /* 300 */ 1344, 1628, 1286, 1606, 1281, 1377, 1377, 1377, 1367, 1367, + /* 310 */ 1367, 1367, 1371, 1371, 1467, 1344, 1337, 1236, 1631, 1631, + /* 320 */ 1353, 1353, 1630, 1630, 1353, 1487, 1614, 1412, 1314, 1320, + /* 330 */ 1320, 1320, 1320, 1353, 1254, 1390, 1614, 1614, 1390, 1412, + /* 340 */ 1314, 1390, 1314, 1390, 1353, 1254, 1503, 1625, 1353, 1254, /* 350 */ 1477, 1353, 1254, 1353, 1254, 1477, 1312, 1312, 1312, 1301, - /* 360 */ 1236, 1236, 1477, 1312, 1286, 1312, 1301, 1312, 1312, 1575, - /* 370 */ 1236, 1481, 1481, 1477, 1353, 1567, 1567, 1380, 1380, 1385, - /* 380 */ 1371, 1472, 1353, 1236, 1385, 1383, 1381, 1390, 1304, 1589, - /* 390 */ 1589, 1585, 1585, 1585, 1638, 1638, 1536, 1601, 1269, 1269, - /* 400 */ 1269, 1269, 1601, 1288, 1288, 1270, 1270, 1269, 1601, 1236, - /* 410 */ 1236, 1236, 1236, 1236, 1236, 1596, 1236, 1531, 1488, 1357, + /* 360 */ 1236, 1236, 1477, 1312, 1286, 1312, 1301, 1312, 1312, 1573, + /* 370 */ 1236, 1481, 1481, 1477, 1353, 1565, 1565, 1380, 1380, 1385, + /* 380 */ 1371, 1472, 1353, 1236, 1385, 1383, 1381, 1390, 1304, 1587, + /* 390 */ 1587, 1583, 1583, 1583, 1636, 1636, 1536, 1599, 1269, 1269, + /* 400 */ 1269, 1269, 1599, 1288, 1288, 1270, 1270, 1269, 1599, 1236, + /* 410 */ 1236, 1236, 1236, 1236, 1236, 1594, 1236, 1531, 1488, 1357, /* 420 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, /* 430 */ 1236, 1236, 1236, 1236, 1542, 1236, 1236, 1236, 1236, 1236, /* 440 */ 1236, 1236, 1236, 1236, 1236, 1417, 1236, 1239, 1533, 1236, /* 450 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1394, 1395, 1358, /* 460 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1409, 1236, 1236, /* 470 */ 1236, 1404, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 480 */ 1629, 1236, 1236, 1236, 1236, 1236, 1236, 1502, 1501, 1236, + /* 480 */ 1627, 1236, 1236, 1236, 1236, 1236, 1236, 1502, 1501, 1236, /* 490 */ 1236, 1355, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, /* 500 */ 1236, 1236, 1236, 1236, 1236, 1284, 1236, 1236, 1236, 1236, /* 510 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, /* 520 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1382, /* 530 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 540 */ 1236, 1236, 1236, 1236, 1572, 1372, 1236, 1236, 1236, 1236, - /* 550 */ 1620, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 560 */ 1236, 1236, 1236, 1236, 1236, 1612, 1328, 1418, 1236, 1421, + /* 540 */ 1236, 1236, 1236, 1236, 1570, 1372, 1236, 1236, 1236, 1236, + /* 550 */ 1618, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 560 */ 1236, 1236, 1236, 1236, 1236, 1610, 1328, 1418, 1236, 1421, /* 570 */ 1258, 1236, 1248, 1236, 1236, }; /********** End of lemon-generated parsing tables *****************************/ @@ -169845,100 +171350,100 @@ static const char *const yyRuleName[] = { /* 306 */ "wqitem ::= nm eidlist_opt wqas LP select RP", /* 307 */ "wqlist ::= wqitem", /* 308 */ "wqlist ::= wqlist COMMA wqitem", - /* 309 */ "windowdefn_list ::= windowdefn", - /* 310 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", - /* 311 */ "windowdefn ::= nm AS LP window RP", - /* 312 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", - /* 313 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", - /* 314 */ "window ::= ORDER BY sortlist frame_opt", - /* 315 */ "window ::= nm ORDER BY sortlist frame_opt", - /* 316 */ "window ::= frame_opt", - /* 317 */ "window ::= nm frame_opt", - /* 318 */ "frame_opt ::=", - /* 319 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", - /* 320 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", - /* 321 */ "range_or_rows ::= RANGE|ROWS|GROUPS", - /* 322 */ "frame_bound_s ::= frame_bound", - /* 323 */ "frame_bound_s ::= UNBOUNDED PRECEDING", - /* 324 */ "frame_bound_e ::= frame_bound", - /* 325 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", - /* 326 */ "frame_bound ::= expr PRECEDING|FOLLOWING", - /* 327 */ "frame_bound ::= CURRENT ROW", - /* 328 */ "frame_exclude_opt ::=", - /* 329 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", - /* 330 */ "frame_exclude ::= NO OTHERS", - /* 331 */ "frame_exclude ::= CURRENT ROW", - /* 332 */ "frame_exclude ::= GROUP|TIES", - /* 333 */ "window_clause ::= WINDOW windowdefn_list", - /* 334 */ "filter_over ::= filter_clause over_clause", - /* 335 */ "filter_over ::= over_clause", - /* 336 */ "filter_over ::= filter_clause", - /* 337 */ "over_clause ::= OVER LP window RP", - /* 338 */ "over_clause ::= OVER nm", - /* 339 */ "filter_clause ::= FILTER LP WHERE expr RP", - /* 340 */ "input ::= cmdlist", - /* 341 */ "cmdlist ::= cmdlist ecmd", - /* 342 */ "cmdlist ::= ecmd", - /* 343 */ "ecmd ::= SEMI", - /* 344 */ "ecmd ::= cmdx SEMI", - /* 345 */ "ecmd ::= explain cmdx SEMI", - /* 346 */ "trans_opt ::=", - /* 347 */ "trans_opt ::= TRANSACTION", - /* 348 */ "trans_opt ::= TRANSACTION nm", - /* 349 */ "savepoint_opt ::= SAVEPOINT", - /* 350 */ "savepoint_opt ::=", - /* 351 */ "cmd ::= create_table create_table_args", - /* 352 */ "table_option_set ::= table_option", - /* 353 */ "columnlist ::= columnlist COMMA columnname carglist", - /* 354 */ "columnlist ::= columnname carglist", - /* 355 */ "nm ::= ID|INDEXED|JOIN_KW", - /* 356 */ "nm ::= STRING", - /* 357 */ "typetoken ::= typename", - /* 358 */ "typename ::= ID|STRING", - /* 359 */ "signed ::= plus_num", - /* 360 */ "signed ::= minus_num", - /* 361 */ "carglist ::= carglist ccons", - /* 362 */ "carglist ::=", - /* 363 */ "ccons ::= NULL onconf", - /* 364 */ "ccons ::= GENERATED ALWAYS AS generated", - /* 365 */ "ccons ::= AS generated", - /* 366 */ "conslist_opt ::= COMMA conslist", - /* 367 */ "conslist ::= conslist tconscomma tcons", - /* 368 */ "conslist ::= tcons", - /* 369 */ "tconscomma ::=", - /* 370 */ "defer_subclause_opt ::= defer_subclause", - /* 371 */ "resolvetype ::= raisetype", - /* 372 */ "selectnowith ::= oneselect", - /* 373 */ "oneselect ::= values", - /* 374 */ "sclp ::= selcollist COMMA", - /* 375 */ "as ::= ID|STRING", - /* 376 */ "indexed_opt ::= indexed_by", - /* 377 */ "returning ::=", - /* 378 */ "expr ::= term", - /* 379 */ "likeop ::= LIKE_KW|MATCH", - /* 380 */ "case_operand ::= expr", - /* 381 */ "exprlist ::= nexprlist", - /* 382 */ "nmnum ::= plus_num", - /* 383 */ "nmnum ::= nm", - /* 384 */ "nmnum ::= ON", - /* 385 */ "nmnum ::= DELETE", - /* 386 */ "nmnum ::= DEFAULT", - /* 387 */ "plus_num ::= INTEGER|FLOAT", - /* 388 */ "foreach_clause ::=", - /* 389 */ "foreach_clause ::= FOR EACH ROW", - /* 390 */ "trnm ::= nm", - /* 391 */ "tridxby ::=", - /* 392 */ "database_kw_opt ::= DATABASE", - /* 393 */ "database_kw_opt ::=", - /* 394 */ "kwcolumn_opt ::=", - /* 395 */ "kwcolumn_opt ::= COLUMNKW", - /* 396 */ "vtabarglist ::= vtabarg", - /* 397 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 398 */ "vtabarg ::= vtabarg vtabargtoken", - /* 399 */ "anylist ::=", - /* 400 */ "anylist ::= anylist LP anylist RP", - /* 401 */ "anylist ::= anylist ANY", - /* 402 */ "with ::=", + /* 309 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", + /* 310 */ "windowdefn ::= nm AS LP window RP", + /* 311 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", + /* 312 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", + /* 313 */ "window ::= ORDER BY sortlist frame_opt", + /* 314 */ "window ::= nm ORDER BY sortlist frame_opt", + /* 315 */ "window ::= nm frame_opt", + /* 316 */ "frame_opt ::=", + /* 317 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", + /* 318 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", + /* 319 */ "range_or_rows ::= RANGE|ROWS|GROUPS", + /* 320 */ "frame_bound_s ::= frame_bound", + /* 321 */ "frame_bound_s ::= UNBOUNDED PRECEDING", + /* 322 */ "frame_bound_e ::= frame_bound", + /* 323 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", + /* 324 */ "frame_bound ::= expr PRECEDING|FOLLOWING", + /* 325 */ "frame_bound ::= CURRENT ROW", + /* 326 */ "frame_exclude_opt ::=", + /* 327 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", + /* 328 */ "frame_exclude ::= NO OTHERS", + /* 329 */ "frame_exclude ::= CURRENT ROW", + /* 330 */ "frame_exclude ::= GROUP|TIES", + /* 331 */ "window_clause ::= WINDOW windowdefn_list", + /* 332 */ "filter_over ::= filter_clause over_clause", + /* 333 */ "filter_over ::= over_clause", + /* 334 */ "filter_over ::= filter_clause", + /* 335 */ "over_clause ::= OVER LP window RP", + /* 336 */ "over_clause ::= OVER nm", + /* 337 */ "filter_clause ::= FILTER LP WHERE expr RP", + /* 338 */ "input ::= cmdlist", + /* 339 */ "cmdlist ::= cmdlist ecmd", + /* 340 */ "cmdlist ::= ecmd", + /* 341 */ "ecmd ::= SEMI", + /* 342 */ "ecmd ::= cmdx SEMI", + /* 343 */ "ecmd ::= explain cmdx SEMI", + /* 344 */ "trans_opt ::=", + /* 345 */ "trans_opt ::= TRANSACTION", + /* 346 */ "trans_opt ::= TRANSACTION nm", + /* 347 */ "savepoint_opt ::= SAVEPOINT", + /* 348 */ "savepoint_opt ::=", + /* 349 */ "cmd ::= create_table create_table_args", + /* 350 */ "table_option_set ::= table_option", + /* 351 */ "columnlist ::= columnlist COMMA columnname carglist", + /* 352 */ "columnlist ::= columnname carglist", + /* 353 */ "nm ::= ID|INDEXED|JOIN_KW", + /* 354 */ "nm ::= STRING", + /* 355 */ "typetoken ::= typename", + /* 356 */ "typename ::= ID|STRING", + /* 357 */ "signed ::= plus_num", + /* 358 */ "signed ::= minus_num", + /* 359 */ "carglist ::= carglist ccons", + /* 360 */ "carglist ::=", + /* 361 */ "ccons ::= NULL onconf", + /* 362 */ "ccons ::= GENERATED ALWAYS AS generated", + /* 363 */ "ccons ::= AS generated", + /* 364 */ "conslist_opt ::= COMMA conslist", + /* 365 */ "conslist ::= conslist tconscomma tcons", + /* 366 */ "conslist ::= tcons", + /* 367 */ "tconscomma ::=", + /* 368 */ "defer_subclause_opt ::= defer_subclause", + /* 369 */ "resolvetype ::= raisetype", + /* 370 */ "selectnowith ::= oneselect", + /* 371 */ "oneselect ::= values", + /* 372 */ "sclp ::= selcollist COMMA", + /* 373 */ "as ::= ID|STRING", + /* 374 */ "indexed_opt ::= indexed_by", + /* 375 */ "returning ::=", + /* 376 */ "expr ::= term", + /* 377 */ "likeop ::= LIKE_KW|MATCH", + /* 378 */ "case_operand ::= expr", + /* 379 */ "exprlist ::= nexprlist", + /* 380 */ "nmnum ::= plus_num", + /* 381 */ "nmnum ::= nm", + /* 382 */ "nmnum ::= ON", + /* 383 */ "nmnum ::= DELETE", + /* 384 */ "nmnum ::= DEFAULT", + /* 385 */ "plus_num ::= INTEGER|FLOAT", + /* 386 */ "foreach_clause ::=", + /* 387 */ "foreach_clause ::= FOR EACH ROW", + /* 388 */ "trnm ::= nm", + /* 389 */ "tridxby ::=", + /* 390 */ "database_kw_opt ::= DATABASE", + /* 391 */ "database_kw_opt ::=", + /* 392 */ "kwcolumn_opt ::=", + /* 393 */ "kwcolumn_opt ::= COLUMNKW", + /* 394 */ "vtabarglist ::= vtabarg", + /* 395 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 396 */ "vtabarg ::= vtabarg vtabargtoken", + /* 397 */ "anylist ::=", + /* 398 */ "anylist ::= anylist LP anylist RP", + /* 399 */ "anylist ::= anylist ANY", + /* 400 */ "with ::=", + /* 401 */ "windowdefn_list ::= windowdefn", + /* 402 */ "window ::= frame_opt", }; #endif /* NDEBUG */ @@ -170754,100 +172259,100 @@ static const YYCODETYPE yyRuleInfoLhs[] = { 304, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */ 241, /* (307) wqlist ::= wqitem */ 241, /* (308) wqlist ::= wqlist COMMA wqitem */ - 306, /* (309) windowdefn_list ::= windowdefn */ - 306, /* (310) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - 307, /* (311) windowdefn ::= nm AS LP window RP */ - 308, /* (312) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - 308, /* (313) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - 308, /* (314) window ::= ORDER BY sortlist frame_opt */ - 308, /* (315) window ::= nm ORDER BY sortlist frame_opt */ - 308, /* (316) window ::= frame_opt */ - 308, /* (317) window ::= nm frame_opt */ - 309, /* (318) frame_opt ::= */ - 309, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - 309, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - 313, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */ - 315, /* (322) frame_bound_s ::= frame_bound */ - 315, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */ - 316, /* (324) frame_bound_e ::= frame_bound */ - 316, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */ - 314, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */ - 314, /* (327) frame_bound ::= CURRENT ROW */ - 317, /* (328) frame_exclude_opt ::= */ - 317, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */ - 318, /* (330) frame_exclude ::= NO OTHERS */ - 318, /* (331) frame_exclude ::= CURRENT ROW */ - 318, /* (332) frame_exclude ::= GROUP|TIES */ - 251, /* (333) window_clause ::= WINDOW windowdefn_list */ - 273, /* (334) filter_over ::= filter_clause over_clause */ - 273, /* (335) filter_over ::= over_clause */ - 273, /* (336) filter_over ::= filter_clause */ - 312, /* (337) over_clause ::= OVER LP window RP */ - 312, /* (338) over_clause ::= OVER nm */ - 311, /* (339) filter_clause ::= FILTER LP WHERE expr RP */ - 185, /* (340) input ::= cmdlist */ - 186, /* (341) cmdlist ::= cmdlist ecmd */ - 186, /* (342) cmdlist ::= ecmd */ - 187, /* (343) ecmd ::= SEMI */ - 187, /* (344) ecmd ::= cmdx SEMI */ - 187, /* (345) ecmd ::= explain cmdx SEMI */ - 192, /* (346) trans_opt ::= */ - 192, /* (347) trans_opt ::= TRANSACTION */ - 192, /* (348) trans_opt ::= TRANSACTION nm */ - 194, /* (349) savepoint_opt ::= SAVEPOINT */ - 194, /* (350) savepoint_opt ::= */ - 190, /* (351) cmd ::= create_table create_table_args */ - 203, /* (352) table_option_set ::= table_option */ - 201, /* (353) columnlist ::= columnlist COMMA columnname carglist */ - 201, /* (354) columnlist ::= columnname carglist */ - 193, /* (355) nm ::= ID|INDEXED|JOIN_KW */ - 193, /* (356) nm ::= STRING */ - 208, /* (357) typetoken ::= typename */ - 209, /* (358) typename ::= ID|STRING */ - 210, /* (359) signed ::= plus_num */ - 210, /* (360) signed ::= minus_num */ - 207, /* (361) carglist ::= carglist ccons */ - 207, /* (362) carglist ::= */ - 215, /* (363) ccons ::= NULL onconf */ - 215, /* (364) ccons ::= GENERATED ALWAYS AS generated */ - 215, /* (365) ccons ::= AS generated */ - 202, /* (366) conslist_opt ::= COMMA conslist */ - 228, /* (367) conslist ::= conslist tconscomma tcons */ - 228, /* (368) conslist ::= tcons */ - 229, /* (369) tconscomma ::= */ - 233, /* (370) defer_subclause_opt ::= defer_subclause */ - 235, /* (371) resolvetype ::= raisetype */ - 239, /* (372) selectnowith ::= oneselect */ - 240, /* (373) oneselect ::= values */ - 254, /* (374) sclp ::= selcollist COMMA */ - 255, /* (375) as ::= ID|STRING */ - 264, /* (376) indexed_opt ::= indexed_by */ - 272, /* (377) returning ::= */ - 217, /* (378) expr ::= term */ - 274, /* (379) likeop ::= LIKE_KW|MATCH */ - 278, /* (380) case_operand ::= expr */ - 261, /* (381) exprlist ::= nexprlist */ - 284, /* (382) nmnum ::= plus_num */ - 284, /* (383) nmnum ::= nm */ - 284, /* (384) nmnum ::= ON */ - 284, /* (385) nmnum ::= DELETE */ - 284, /* (386) nmnum ::= DEFAULT */ - 211, /* (387) plus_num ::= INTEGER|FLOAT */ - 289, /* (388) foreach_clause ::= */ - 289, /* (389) foreach_clause ::= FOR EACH ROW */ - 292, /* (390) trnm ::= nm */ - 293, /* (391) tridxby ::= */ - 294, /* (392) database_kw_opt ::= DATABASE */ - 294, /* (393) database_kw_opt ::= */ - 297, /* (394) kwcolumn_opt ::= */ - 297, /* (395) kwcolumn_opt ::= COLUMNKW */ - 299, /* (396) vtabarglist ::= vtabarg */ - 299, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */ - 300, /* (398) vtabarg ::= vtabarg vtabargtoken */ - 303, /* (399) anylist ::= */ - 303, /* (400) anylist ::= anylist LP anylist RP */ - 303, /* (401) anylist ::= anylist ANY */ - 266, /* (402) with ::= */ + 306, /* (309) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + 307, /* (310) windowdefn ::= nm AS LP window RP */ + 308, /* (311) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + 308, /* (312) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + 308, /* (313) window ::= ORDER BY sortlist frame_opt */ + 308, /* (314) window ::= nm ORDER BY sortlist frame_opt */ + 308, /* (315) window ::= nm frame_opt */ + 309, /* (316) frame_opt ::= */ + 309, /* (317) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + 309, /* (318) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + 313, /* (319) range_or_rows ::= RANGE|ROWS|GROUPS */ + 315, /* (320) frame_bound_s ::= frame_bound */ + 315, /* (321) frame_bound_s ::= UNBOUNDED PRECEDING */ + 316, /* (322) frame_bound_e ::= frame_bound */ + 316, /* (323) frame_bound_e ::= UNBOUNDED FOLLOWING */ + 314, /* (324) frame_bound ::= expr PRECEDING|FOLLOWING */ + 314, /* (325) frame_bound ::= CURRENT ROW */ + 317, /* (326) frame_exclude_opt ::= */ + 317, /* (327) frame_exclude_opt ::= EXCLUDE frame_exclude */ + 318, /* (328) frame_exclude ::= NO OTHERS */ + 318, /* (329) frame_exclude ::= CURRENT ROW */ + 318, /* (330) frame_exclude ::= GROUP|TIES */ + 251, /* (331) window_clause ::= WINDOW windowdefn_list */ + 273, /* (332) filter_over ::= filter_clause over_clause */ + 273, /* (333) filter_over ::= over_clause */ + 273, /* (334) filter_over ::= filter_clause */ + 312, /* (335) over_clause ::= OVER LP window RP */ + 312, /* (336) over_clause ::= OVER nm */ + 311, /* (337) filter_clause ::= FILTER LP WHERE expr RP */ + 185, /* (338) input ::= cmdlist */ + 186, /* (339) cmdlist ::= cmdlist ecmd */ + 186, /* (340) cmdlist ::= ecmd */ + 187, /* (341) ecmd ::= SEMI */ + 187, /* (342) ecmd ::= cmdx SEMI */ + 187, /* (343) ecmd ::= explain cmdx SEMI */ + 192, /* (344) trans_opt ::= */ + 192, /* (345) trans_opt ::= TRANSACTION */ + 192, /* (346) trans_opt ::= TRANSACTION nm */ + 194, /* (347) savepoint_opt ::= SAVEPOINT */ + 194, /* (348) savepoint_opt ::= */ + 190, /* (349) cmd ::= create_table create_table_args */ + 203, /* (350) table_option_set ::= table_option */ + 201, /* (351) columnlist ::= columnlist COMMA columnname carglist */ + 201, /* (352) columnlist ::= columnname carglist */ + 193, /* (353) nm ::= ID|INDEXED|JOIN_KW */ + 193, /* (354) nm ::= STRING */ + 208, /* (355) typetoken ::= typename */ + 209, /* (356) typename ::= ID|STRING */ + 210, /* (357) signed ::= plus_num */ + 210, /* (358) signed ::= minus_num */ + 207, /* (359) carglist ::= carglist ccons */ + 207, /* (360) carglist ::= */ + 215, /* (361) ccons ::= NULL onconf */ + 215, /* (362) ccons ::= GENERATED ALWAYS AS generated */ + 215, /* (363) ccons ::= AS generated */ + 202, /* (364) conslist_opt ::= COMMA conslist */ + 228, /* (365) conslist ::= conslist tconscomma tcons */ + 228, /* (366) conslist ::= tcons */ + 229, /* (367) tconscomma ::= */ + 233, /* (368) defer_subclause_opt ::= defer_subclause */ + 235, /* (369) resolvetype ::= raisetype */ + 239, /* (370) selectnowith ::= oneselect */ + 240, /* (371) oneselect ::= values */ + 254, /* (372) sclp ::= selcollist COMMA */ + 255, /* (373) as ::= ID|STRING */ + 264, /* (374) indexed_opt ::= indexed_by */ + 272, /* (375) returning ::= */ + 217, /* (376) expr ::= term */ + 274, /* (377) likeop ::= LIKE_KW|MATCH */ + 278, /* (378) case_operand ::= expr */ + 261, /* (379) exprlist ::= nexprlist */ + 284, /* (380) nmnum ::= plus_num */ + 284, /* (381) nmnum ::= nm */ + 284, /* (382) nmnum ::= ON */ + 284, /* (383) nmnum ::= DELETE */ + 284, /* (384) nmnum ::= DEFAULT */ + 211, /* (385) plus_num ::= INTEGER|FLOAT */ + 289, /* (386) foreach_clause ::= */ + 289, /* (387) foreach_clause ::= FOR EACH ROW */ + 292, /* (388) trnm ::= nm */ + 293, /* (389) tridxby ::= */ + 294, /* (390) database_kw_opt ::= DATABASE */ + 294, /* (391) database_kw_opt ::= */ + 297, /* (392) kwcolumn_opt ::= */ + 297, /* (393) kwcolumn_opt ::= COLUMNKW */ + 299, /* (394) vtabarglist ::= vtabarg */ + 299, /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */ + 300, /* (396) vtabarg ::= vtabarg vtabargtoken */ + 303, /* (397) anylist ::= */ + 303, /* (398) anylist ::= anylist LP anylist RP */ + 303, /* (399) anylist ::= anylist ANY */ + 266, /* (400) with ::= */ + 306, /* (401) windowdefn_list ::= windowdefn */ + 308, /* (402) window ::= frame_opt */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number @@ -171162,100 +172667,100 @@ static const signed char yyRuleInfoNRhs[] = { -6, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */ -1, /* (307) wqlist ::= wqitem */ -3, /* (308) wqlist ::= wqlist COMMA wqitem */ - -1, /* (309) windowdefn_list ::= windowdefn */ - -3, /* (310) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - -5, /* (311) windowdefn ::= nm AS LP window RP */ - -5, /* (312) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - -6, /* (313) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - -4, /* (314) window ::= ORDER BY sortlist frame_opt */ - -5, /* (315) window ::= nm ORDER BY sortlist frame_opt */ - -1, /* (316) window ::= frame_opt */ - -2, /* (317) window ::= nm frame_opt */ - 0, /* (318) frame_opt ::= */ - -3, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - -6, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - -1, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */ - -1, /* (322) frame_bound_s ::= frame_bound */ - -2, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */ - -1, /* (324) frame_bound_e ::= frame_bound */ - -2, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */ - -2, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */ - -2, /* (327) frame_bound ::= CURRENT ROW */ - 0, /* (328) frame_exclude_opt ::= */ - -2, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */ - -2, /* (330) frame_exclude ::= NO OTHERS */ - -2, /* (331) frame_exclude ::= CURRENT ROW */ - -1, /* (332) frame_exclude ::= GROUP|TIES */ - -2, /* (333) window_clause ::= WINDOW windowdefn_list */ - -2, /* (334) filter_over ::= filter_clause over_clause */ - -1, /* (335) filter_over ::= over_clause */ - -1, /* (336) filter_over ::= filter_clause */ - -4, /* (337) over_clause ::= OVER LP window RP */ - -2, /* (338) over_clause ::= OVER nm */ - -5, /* (339) filter_clause ::= FILTER LP WHERE expr RP */ - -1, /* (340) input ::= cmdlist */ - -2, /* (341) cmdlist ::= cmdlist ecmd */ - -1, /* (342) cmdlist ::= ecmd */ - -1, /* (343) ecmd ::= SEMI */ - -2, /* (344) ecmd ::= cmdx SEMI */ - -3, /* (345) ecmd ::= explain cmdx SEMI */ - 0, /* (346) trans_opt ::= */ - -1, /* (347) trans_opt ::= TRANSACTION */ - -2, /* (348) trans_opt ::= TRANSACTION nm */ - -1, /* (349) savepoint_opt ::= SAVEPOINT */ - 0, /* (350) savepoint_opt ::= */ - -2, /* (351) cmd ::= create_table create_table_args */ - -1, /* (352) table_option_set ::= table_option */ - -4, /* (353) columnlist ::= columnlist COMMA columnname carglist */ - -2, /* (354) columnlist ::= columnname carglist */ - -1, /* (355) nm ::= ID|INDEXED|JOIN_KW */ - -1, /* (356) nm ::= STRING */ - -1, /* (357) typetoken ::= typename */ - -1, /* (358) typename ::= ID|STRING */ - -1, /* (359) signed ::= plus_num */ - -1, /* (360) signed ::= minus_num */ - -2, /* (361) carglist ::= carglist ccons */ - 0, /* (362) carglist ::= */ - -2, /* (363) ccons ::= NULL onconf */ - -4, /* (364) ccons ::= GENERATED ALWAYS AS generated */ - -2, /* (365) ccons ::= AS generated */ - -2, /* (366) conslist_opt ::= COMMA conslist */ - -3, /* (367) conslist ::= conslist tconscomma tcons */ - -1, /* (368) conslist ::= tcons */ - 0, /* (369) tconscomma ::= */ - -1, /* (370) defer_subclause_opt ::= defer_subclause */ - -1, /* (371) resolvetype ::= raisetype */ - -1, /* (372) selectnowith ::= oneselect */ - -1, /* (373) oneselect ::= values */ - -2, /* (374) sclp ::= selcollist COMMA */ - -1, /* (375) as ::= ID|STRING */ - -1, /* (376) indexed_opt ::= indexed_by */ - 0, /* (377) returning ::= */ - -1, /* (378) expr ::= term */ - -1, /* (379) likeop ::= LIKE_KW|MATCH */ - -1, /* (380) case_operand ::= expr */ - -1, /* (381) exprlist ::= nexprlist */ - -1, /* (382) nmnum ::= plus_num */ - -1, /* (383) nmnum ::= nm */ - -1, /* (384) nmnum ::= ON */ - -1, /* (385) nmnum ::= DELETE */ - -1, /* (386) nmnum ::= DEFAULT */ - -1, /* (387) plus_num ::= INTEGER|FLOAT */ - 0, /* (388) foreach_clause ::= */ - -3, /* (389) foreach_clause ::= FOR EACH ROW */ - -1, /* (390) trnm ::= nm */ - 0, /* (391) tridxby ::= */ - -1, /* (392) database_kw_opt ::= DATABASE */ - 0, /* (393) database_kw_opt ::= */ - 0, /* (394) kwcolumn_opt ::= */ - -1, /* (395) kwcolumn_opt ::= COLUMNKW */ - -1, /* (396) vtabarglist ::= vtabarg */ - -3, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */ - -2, /* (398) vtabarg ::= vtabarg vtabargtoken */ - 0, /* (399) anylist ::= */ - -4, /* (400) anylist ::= anylist LP anylist RP */ - -2, /* (401) anylist ::= anylist ANY */ - 0, /* (402) with ::= */ + -3, /* (309) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + -5, /* (310) windowdefn ::= nm AS LP window RP */ + -5, /* (311) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + -6, /* (312) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + -4, /* (313) window ::= ORDER BY sortlist frame_opt */ + -5, /* (314) window ::= nm ORDER BY sortlist frame_opt */ + -2, /* (315) window ::= nm frame_opt */ + 0, /* (316) frame_opt ::= */ + -3, /* (317) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + -6, /* (318) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + -1, /* (319) range_or_rows ::= RANGE|ROWS|GROUPS */ + -1, /* (320) frame_bound_s ::= frame_bound */ + -2, /* (321) frame_bound_s ::= UNBOUNDED PRECEDING */ + -1, /* (322) frame_bound_e ::= frame_bound */ + -2, /* (323) frame_bound_e ::= UNBOUNDED FOLLOWING */ + -2, /* (324) frame_bound ::= expr PRECEDING|FOLLOWING */ + -2, /* (325) frame_bound ::= CURRENT ROW */ + 0, /* (326) frame_exclude_opt ::= */ + -2, /* (327) frame_exclude_opt ::= EXCLUDE frame_exclude */ + -2, /* (328) frame_exclude ::= NO OTHERS */ + -2, /* (329) frame_exclude ::= CURRENT ROW */ + -1, /* (330) frame_exclude ::= GROUP|TIES */ + -2, /* (331) window_clause ::= WINDOW windowdefn_list */ + -2, /* (332) filter_over ::= filter_clause over_clause */ + -1, /* (333) filter_over ::= over_clause */ + -1, /* (334) filter_over ::= filter_clause */ + -4, /* (335) over_clause ::= OVER LP window RP */ + -2, /* (336) over_clause ::= OVER nm */ + -5, /* (337) filter_clause ::= FILTER LP WHERE expr RP */ + -1, /* (338) input ::= cmdlist */ + -2, /* (339) cmdlist ::= cmdlist ecmd */ + -1, /* (340) cmdlist ::= ecmd */ + -1, /* (341) ecmd ::= SEMI */ + -2, /* (342) ecmd ::= cmdx SEMI */ + -3, /* (343) ecmd ::= explain cmdx SEMI */ + 0, /* (344) trans_opt ::= */ + -1, /* (345) trans_opt ::= TRANSACTION */ + -2, /* (346) trans_opt ::= TRANSACTION nm */ + -1, /* (347) savepoint_opt ::= SAVEPOINT */ + 0, /* (348) savepoint_opt ::= */ + -2, /* (349) cmd ::= create_table create_table_args */ + -1, /* (350) table_option_set ::= table_option */ + -4, /* (351) columnlist ::= columnlist COMMA columnname carglist */ + -2, /* (352) columnlist ::= columnname carglist */ + -1, /* (353) nm ::= ID|INDEXED|JOIN_KW */ + -1, /* (354) nm ::= STRING */ + -1, /* (355) typetoken ::= typename */ + -1, /* (356) typename ::= ID|STRING */ + -1, /* (357) signed ::= plus_num */ + -1, /* (358) signed ::= minus_num */ + -2, /* (359) carglist ::= carglist ccons */ + 0, /* (360) carglist ::= */ + -2, /* (361) ccons ::= NULL onconf */ + -4, /* (362) ccons ::= GENERATED ALWAYS AS generated */ + -2, /* (363) ccons ::= AS generated */ + -2, /* (364) conslist_opt ::= COMMA conslist */ + -3, /* (365) conslist ::= conslist tconscomma tcons */ + -1, /* (366) conslist ::= tcons */ + 0, /* (367) tconscomma ::= */ + -1, /* (368) defer_subclause_opt ::= defer_subclause */ + -1, /* (369) resolvetype ::= raisetype */ + -1, /* (370) selectnowith ::= oneselect */ + -1, /* (371) oneselect ::= values */ + -2, /* (372) sclp ::= selcollist COMMA */ + -1, /* (373) as ::= ID|STRING */ + -1, /* (374) indexed_opt ::= indexed_by */ + 0, /* (375) returning ::= */ + -1, /* (376) expr ::= term */ + -1, /* (377) likeop ::= LIKE_KW|MATCH */ + -1, /* (378) case_operand ::= expr */ + -1, /* (379) exprlist ::= nexprlist */ + -1, /* (380) nmnum ::= plus_num */ + -1, /* (381) nmnum ::= nm */ + -1, /* (382) nmnum ::= ON */ + -1, /* (383) nmnum ::= DELETE */ + -1, /* (384) nmnum ::= DEFAULT */ + -1, /* (385) plus_num ::= INTEGER|FLOAT */ + 0, /* (386) foreach_clause ::= */ + -3, /* (387) foreach_clause ::= FOR EACH ROW */ + -1, /* (388) trnm ::= nm */ + 0, /* (389) tridxby ::= */ + -1, /* (390) database_kw_opt ::= DATABASE */ + 0, /* (391) database_kw_opt ::= */ + 0, /* (392) kwcolumn_opt ::= */ + -1, /* (393) kwcolumn_opt ::= COLUMNKW */ + -1, /* (394) vtabarglist ::= vtabarg */ + -3, /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */ + -2, /* (396) vtabarg ::= vtabarg vtabargtoken */ + 0, /* (397) anylist ::= */ + -4, /* (398) anylist ::= anylist LP anylist RP */ + -2, /* (399) anylist ::= anylist ANY */ + 0, /* (400) with ::= */ + -1, /* (401) windowdefn_list ::= windowdefn */ + -1, /* (402) window ::= frame_opt */ }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -171298,10 +172803,10 @@ static YYACTIONTYPE yy_reduce( /********** Begin reduce actions **********************************************/ YYMINORTYPE yylhsminor; case 0: /* explain ::= EXPLAIN */ -{ pParse->explain = 1; } +{ if( pParse->pReprepare==0 ) pParse->explain = 1; } break; case 1: /* explain ::= EXPLAIN QUERY PLAN */ -{ pParse->explain = 2; } +{ if( pParse->pReprepare==0 ) pParse->explain = 2; } break; case 2: /* cmdx ::= cmd */ { sqlite3FinishCoding(pParse); } @@ -171315,7 +172820,7 @@ static YYACTIONTYPE yy_reduce( case 5: /* transtype ::= DEFERRED */ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); - case 321: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==321); + case 319: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==319); {yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/} break; case 8: /* cmd ::= COMMIT|END trans_opt */ @@ -171611,7 +173116,6 @@ static YYACTIONTYPE yy_reduce( if( p ){ parserDoubleLinkSelect(pParse, p); } - yymsp[0].minor.yy47 = p; /*A-overwrites-X*/ } break; case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */ @@ -171703,14 +173207,17 @@ static YYACTIONTYPE yy_reduce( case 101: /* selcollist ::= sclp scanpt STAR */ { Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); + sqlite3ExprSetErrorOffset(p, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy322, p); } break; case 102: /* selcollist ::= sclp scanpt nm DOT STAR */ { - Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); - Expr *pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0); - Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); + Expr *pRight, *pLeft, *pDot; + pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); + sqlite3ExprSetErrorOffset(pRight, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); + pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0); + pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot); } break; @@ -172604,11 +174111,7 @@ static YYACTIONTYPE yy_reduce( yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385); } break; - case 309: /* windowdefn_list ::= windowdefn */ -{ yylhsminor.yy41 = yymsp[0].minor.yy41; } - yymsp[0].minor.yy41 = yylhsminor.yy41; - break; - case 310: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ + case 309: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { assert( yymsp[0].minor.yy41!=0 ); sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41); @@ -172617,7 +174120,7 @@ static YYACTIONTYPE yy_reduce( } yymsp[-2].minor.yy41 = yylhsminor.yy41; break; - case 311: /* windowdefn ::= nm AS LP window RP */ + case 310: /* windowdefn ::= nm AS LP window RP */ { if( ALWAYS(yymsp[-1].minor.yy41) ){ yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); @@ -172626,90 +174129,83 @@ static YYACTIONTYPE yy_reduce( } yymsp[-4].minor.yy41 = yylhsminor.yy41; break; - case 312: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + case 311: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ { yymsp[-4].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0); } break; - case 313: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + case 312: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ { yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0); } yymsp[-5].minor.yy41 = yylhsminor.yy41; break; - case 314: /* window ::= ORDER BY sortlist frame_opt */ + case 313: /* window ::= ORDER BY sortlist frame_opt */ { yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0); } break; - case 315: /* window ::= nm ORDER BY sortlist frame_opt */ + case 314: /* window ::= nm ORDER BY sortlist frame_opt */ { yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0); } yymsp[-4].minor.yy41 = yylhsminor.yy41; break; - case 316: /* window ::= frame_opt */ - case 335: /* filter_over ::= over_clause */ yytestcase(yyruleno==335); -{ - yylhsminor.yy41 = yymsp[0].minor.yy41; -} - yymsp[0].minor.yy41 = yylhsminor.yy41; - break; - case 317: /* window ::= nm frame_opt */ + case 315: /* window ::= nm frame_opt */ { yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0); } yymsp[-1].minor.yy41 = yylhsminor.yy41; break; - case 318: /* frame_opt ::= */ + case 316: /* frame_opt ::= */ { yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); } break; - case 319: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + case 317: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ { yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516); } yymsp[-2].minor.yy41 = yylhsminor.yy41; break; - case 320: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + case 318: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ { yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516); } yymsp[-5].minor.yy41 = yylhsminor.yy41; break; - case 322: /* frame_bound_s ::= frame_bound */ - case 324: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==324); + case 320: /* frame_bound_s ::= frame_bound */ + case 322: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==322); {yylhsminor.yy595 = yymsp[0].minor.yy595;} yymsp[0].minor.yy595 = yylhsminor.yy595; break; - case 323: /* frame_bound_s ::= UNBOUNDED PRECEDING */ - case 325: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==325); - case 327: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==327); + case 321: /* frame_bound_s ::= UNBOUNDED PRECEDING */ + case 323: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==323); + case 325: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==325); {yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;} yymsp[-1].minor.yy595 = yylhsminor.yy595; break; - case 326: /* frame_bound ::= expr PRECEDING|FOLLOWING */ + case 324: /* frame_bound ::= expr PRECEDING|FOLLOWING */ {yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;} yymsp[-1].minor.yy595 = yylhsminor.yy595; break; - case 328: /* frame_exclude_opt ::= */ + case 326: /* frame_exclude_opt ::= */ {yymsp[1].minor.yy516 = 0;} break; - case 329: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ + case 327: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ {yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;} break; - case 330: /* frame_exclude ::= NO OTHERS */ - case 331: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==331); + case 328: /* frame_exclude ::= NO OTHERS */ + case 329: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==329); {yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/} break; - case 332: /* frame_exclude ::= GROUP|TIES */ + case 330: /* frame_exclude ::= GROUP|TIES */ {yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/} break; - case 333: /* window_clause ::= WINDOW windowdefn_list */ + case 331: /* window_clause ::= WINDOW windowdefn_list */ { yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; } break; - case 334: /* filter_over ::= filter_clause over_clause */ + case 332: /* filter_over ::= filter_clause over_clause */ { if( yymsp[0].minor.yy41 ){ yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528; @@ -172720,7 +174216,13 @@ static YYACTIONTYPE yy_reduce( } yymsp[-1].minor.yy41 = yylhsminor.yy41; break; - case 336: /* filter_over ::= filter_clause */ + case 333: /* filter_over ::= over_clause */ +{ + yylhsminor.yy41 = yymsp[0].minor.yy41; +} + yymsp[0].minor.yy41 = yylhsminor.yy41; + break; + case 334: /* filter_over ::= filter_clause */ { yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); if( yylhsminor.yy41 ){ @@ -172732,13 +174234,13 @@ static YYACTIONTYPE yy_reduce( } yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 337: /* over_clause ::= OVER LP window RP */ + case 335: /* over_clause ::= OVER LP window RP */ { yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41; assert( yymsp[-3].minor.yy41!=0 ); } break; - case 338: /* over_clause ::= OVER nm */ + case 336: /* over_clause ::= OVER nm */ { yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); if( yymsp[-1].minor.yy41 ){ @@ -172746,73 +174248,75 @@ static YYACTIONTYPE yy_reduce( } } break; - case 339: /* filter_clause ::= FILTER LP WHERE expr RP */ + case 337: /* filter_clause ::= FILTER LP WHERE expr RP */ { yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; } break; default: - /* (340) input ::= cmdlist */ yytestcase(yyruleno==340); - /* (341) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==341); - /* (342) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=342); - /* (343) ecmd ::= SEMI */ yytestcase(yyruleno==343); - /* (344) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==344); - /* (345) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=345); - /* (346) trans_opt ::= */ yytestcase(yyruleno==346); - /* (347) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==347); - /* (348) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==348); - /* (349) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==349); - /* (350) savepoint_opt ::= */ yytestcase(yyruleno==350); - /* (351) cmd ::= create_table create_table_args */ yytestcase(yyruleno==351); - /* (352) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=352); - /* (353) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==353); - /* (354) columnlist ::= columnname carglist */ yytestcase(yyruleno==354); - /* (355) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==355); - /* (356) nm ::= STRING */ yytestcase(yyruleno==356); - /* (357) typetoken ::= typename */ yytestcase(yyruleno==357); - /* (358) typename ::= ID|STRING */ yytestcase(yyruleno==358); - /* (359) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=359); - /* (360) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=360); - /* (361) carglist ::= carglist ccons */ yytestcase(yyruleno==361); - /* (362) carglist ::= */ yytestcase(yyruleno==362); - /* (363) ccons ::= NULL onconf */ yytestcase(yyruleno==363); - /* (364) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==364); - /* (365) ccons ::= AS generated */ yytestcase(yyruleno==365); - /* (366) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==366); - /* (367) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==367); - /* (368) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=368); - /* (369) tconscomma ::= */ yytestcase(yyruleno==369); - /* (370) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=370); - /* (371) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=371); - /* (372) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=372); - /* (373) oneselect ::= values */ yytestcase(yyruleno==373); - /* (374) sclp ::= selcollist COMMA */ yytestcase(yyruleno==374); - /* (375) as ::= ID|STRING */ yytestcase(yyruleno==375); - /* (376) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=376); - /* (377) returning ::= */ yytestcase(yyruleno==377); - /* (378) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=378); - /* (379) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==379); - /* (380) case_operand ::= expr */ yytestcase(yyruleno==380); - /* (381) exprlist ::= nexprlist */ yytestcase(yyruleno==381); - /* (382) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=382); - /* (383) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=383); - /* (384) nmnum ::= ON */ yytestcase(yyruleno==384); - /* (385) nmnum ::= DELETE */ yytestcase(yyruleno==385); - /* (386) nmnum ::= DEFAULT */ yytestcase(yyruleno==386); - /* (387) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==387); - /* (388) foreach_clause ::= */ yytestcase(yyruleno==388); - /* (389) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==389); - /* (390) trnm ::= nm */ yytestcase(yyruleno==390); - /* (391) tridxby ::= */ yytestcase(yyruleno==391); - /* (392) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==392); - /* (393) database_kw_opt ::= */ yytestcase(yyruleno==393); - /* (394) kwcolumn_opt ::= */ yytestcase(yyruleno==394); - /* (395) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==395); - /* (396) vtabarglist ::= vtabarg */ yytestcase(yyruleno==396); - /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==397); - /* (398) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==398); - /* (399) anylist ::= */ yytestcase(yyruleno==399); - /* (400) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==400); - /* (401) anylist ::= anylist ANY */ yytestcase(yyruleno==401); - /* (402) with ::= */ yytestcase(yyruleno==402); + /* (338) input ::= cmdlist */ yytestcase(yyruleno==338); + /* (339) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==339); + /* (340) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=340); + /* (341) ecmd ::= SEMI */ yytestcase(yyruleno==341); + /* (342) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==342); + /* (343) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=343); + /* (344) trans_opt ::= */ yytestcase(yyruleno==344); + /* (345) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==345); + /* (346) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==346); + /* (347) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==347); + /* (348) savepoint_opt ::= */ yytestcase(yyruleno==348); + /* (349) cmd ::= create_table create_table_args */ yytestcase(yyruleno==349); + /* (350) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=350); + /* (351) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==351); + /* (352) columnlist ::= columnname carglist */ yytestcase(yyruleno==352); + /* (353) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==353); + /* (354) nm ::= STRING */ yytestcase(yyruleno==354); + /* (355) typetoken ::= typename */ yytestcase(yyruleno==355); + /* (356) typename ::= ID|STRING */ yytestcase(yyruleno==356); + /* (357) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=357); + /* (358) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=358); + /* (359) carglist ::= carglist ccons */ yytestcase(yyruleno==359); + /* (360) carglist ::= */ yytestcase(yyruleno==360); + /* (361) ccons ::= NULL onconf */ yytestcase(yyruleno==361); + /* (362) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==362); + /* (363) ccons ::= AS generated */ yytestcase(yyruleno==363); + /* (364) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==364); + /* (365) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==365); + /* (366) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=366); + /* (367) tconscomma ::= */ yytestcase(yyruleno==367); + /* (368) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=368); + /* (369) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=369); + /* (370) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=370); + /* (371) oneselect ::= values */ yytestcase(yyruleno==371); + /* (372) sclp ::= selcollist COMMA */ yytestcase(yyruleno==372); + /* (373) as ::= ID|STRING */ yytestcase(yyruleno==373); + /* (374) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=374); + /* (375) returning ::= */ yytestcase(yyruleno==375); + /* (376) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=376); + /* (377) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==377); + /* (378) case_operand ::= expr */ yytestcase(yyruleno==378); + /* (379) exprlist ::= nexprlist */ yytestcase(yyruleno==379); + /* (380) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=380); + /* (381) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=381); + /* (382) nmnum ::= ON */ yytestcase(yyruleno==382); + /* (383) nmnum ::= DELETE */ yytestcase(yyruleno==383); + /* (384) nmnum ::= DEFAULT */ yytestcase(yyruleno==384); + /* (385) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==385); + /* (386) foreach_clause ::= */ yytestcase(yyruleno==386); + /* (387) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==387); + /* (388) trnm ::= nm */ yytestcase(yyruleno==388); + /* (389) tridxby ::= */ yytestcase(yyruleno==389); + /* (390) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==390); + /* (391) database_kw_opt ::= */ yytestcase(yyruleno==391); + /* (392) kwcolumn_opt ::= */ yytestcase(yyruleno==392); + /* (393) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==393); + /* (394) vtabarglist ::= vtabarg */ yytestcase(yyruleno==394); + /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==395); + /* (396) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==396); + /* (397) anylist ::= */ yytestcase(yyruleno==397); + /* (398) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==398); + /* (399) anylist ::= anylist ANY */ yytestcase(yyruleno==399); + /* (400) with ::= */ yytestcase(yyruleno==400); + /* (401) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=401); + /* (402) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=402); break; /********** End reduce actions ************************************************/ }; @@ -173601,180 +175105,179 @@ static const unsigned char aKWCode[148] = {0, static int keywordCode(const char *z, int n, int *pType){ int i, j; const char *zKW; - if( n>=2 ){ - i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n*1) % 127; - for(i=(int)aKWHash[i]; i>0; i=aKWNext[i]){ - if( aKWLen[i]!=n ) continue; - zKW = &zKWText[aKWOffset[i]]; + assert( n>=2 ); + i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n*1) % 127; + for(i=(int)aKWHash[i]; i>0; i=aKWNext[i]){ + if( aKWLen[i]!=n ) continue; + zKW = &zKWText[aKWOffset[i]]; #ifdef SQLITE_ASCII - if( (z[0]&~0x20)!=zKW[0] ) continue; - if( (z[1]&~0x20)!=zKW[1] ) continue; - j = 2; - while( j=2 ) keywordCode((char*)z, n, &id); return id; } #define SQLITE_N_KEYWORD 147 @@ -174079,7 +175582,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' ); testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); - testcase( z[0]=='9' ); + testcase( z[0]=='9' ); testcase( z[0]=='.' ); *tokenType = TK_INTEGER; #ifndef SQLITE_OMIT_HEX_INTEGER if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){ @@ -174151,7 +175654,8 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ return i; } case CC_KYWD0: { - for(i=1; aiClass[z[i]]<=CC_KYWD; i++){} + if( aiClass[z[1]]>CC_KYWD ){ i = 1; break; } + for(i=2; aiClass[z[i]]<=CC_KYWD; i++){} if( IdChar(z[i]) ){ /* This token started out using characters that can appear in keywords, ** but z[i] is a character not allowed within keywords, so this must @@ -174930,12 +176434,6 @@ static int sqlite3TestExtInit(sqlite3 *db){ ** Forward declarations of external module initializer functions ** for modules that need them. */ -#ifdef SQLITE_ENABLE_FTS1 -SQLITE_PRIVATE int sqlite3Fts1Init(sqlite3*); -#endif -#ifdef SQLITE_ENABLE_FTS2 -SQLITE_PRIVATE int sqlite3Fts2Init(sqlite3*); -#endif #ifdef SQLITE_ENABLE_FTS5 SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*); #endif @@ -174948,12 +176446,6 @@ SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*); ** built-in extensions. */ static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { -#ifdef SQLITE_ENABLE_FTS1 - sqlite3Fts1Init, -#endif -#ifdef SQLITE_ENABLE_FTS2 - sqlite3Fts2Init, -#endif #ifdef SQLITE_ENABLE_FTS3 sqlite3Fts3Init, #endif @@ -176566,9 +178058,9 @@ static int sqliteDefaultBusyCallback( void *ptr, /* Database connection */ int count /* Number of times table has been busy */ ){ -#if SQLITE_OS_WIN || HAVE_USLEEP +#if SQLITE_OS_WIN || !defined(HAVE_NANOSLEEP) || HAVE_NANOSLEEP /* This case is for systems that have support for sleeping for fractions of - ** a second. Examples: All windows systems, unix systems with usleep() */ + ** a second. Examples: All windows systems, unix systems with nanosleep() */ static const u8 delays[] = { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; static const u8 totals[] = @@ -178206,7 +179698,7 @@ static int openDatabase( ** 0 off off ** ** Legacy behavior is 3 (double-quoted string literals are allowed anywhere) -** and so that is the default. But developers are encouranged to use +** and so that is the default. But developers are encouraged to use ** -DSQLITE_DQS=0 (best) or -DSQLITE_DQS=1 (second choice) if possible. */ #if !defined(SQLITE_DQS) @@ -178741,7 +180233,7 @@ SQLITE_API int sqlite3_table_column_metadata( /* Find the column for which info is requested */ if( zColumnName==0 ){ - /* Query for existance of table only */ + /* Query for existence of table only */ }else{ for(iCol=0; iColnCol; iCol++){ pCol = &pTab->aCol[iCol]; @@ -179061,10 +180553,12 @@ SQLITE_API int sqlite3_test_control(int op, ...){ sqlite3ShowSrcList(0); sqlite3ShowWith(0); sqlite3ShowUpsert(0); +#ifndef SQLITE_OMIT_TRIGGER sqlite3ShowTriggerStep(0); sqlite3ShowTriggerStepList(0); sqlite3ShowTrigger(0); sqlite3ShowTriggerList(0); +#endif #ifndef SQLITE_OMIT_WINDOWFUNC sqlite3ShowWindow(0); sqlite3ShowWinFunc(0); @@ -179181,7 +180675,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){ ** formed and never corrupt. This flag is clear by default, indicating that ** database files might have arbitrary corruption. Setting the flag during ** testing causes certain assert() statements in the code to be activated - ** that demonstrat invariants on well-formed database files. + ** that demonstrate invariants on well-formed database files. */ case SQLITE_TESTCTRL_NEVER_CORRUPT: { sqlite3GlobalConfig.neverCorrupt = va_arg(ap, int); @@ -179335,7 +180829,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){ ** ** op==0 Store the current sqlite3TreeTrace in *ptr ** op==1 Set sqlite3TreeTrace to the value *ptr - ** op==3 Store the current sqlite3WhereTrace in *ptr + ** op==2 Store the current sqlite3WhereTrace in *ptr ** op==3 Set sqlite3WhereTrace to the value *ptr */ case SQLITE_TESTCTRL_TRACEFLAGS: { @@ -179371,6 +180865,23 @@ SQLITE_API int sqlite3_test_control(int op, ...){ break; } +#if !defined(SQLITE_OMIT_WSD) + /* sqlite3_test_control(SQLITE_TESTCTRL_USELONGDOUBLE, int X); + ** + ** X<0 Make no changes to the bUseLongDouble. Just report value. + ** X==0 Disable bUseLongDouble + ** X==1 Enable bUseLongDouble + ** X==2 Set bUseLongDouble to its default value for this platform + */ + case SQLITE_TESTCTRL_USELONGDOUBLE: { + int b = va_arg(ap, int); + if( b==2 ) b = sizeof(LONGDOUBLE_TYPE)>8; + if( b>=0 ) sqlite3Config.bUseLongDouble = b>0; + rc = sqlite3Config.bUseLongDouble!=0; + break; + } +#endif + #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) /* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue) @@ -179671,7 +181182,7 @@ SQLITE_API int sqlite3_snapshot_get( } /* -** Open a read-transaction on the snapshot idendified by pSnapshot. +** Open a read-transaction on the snapshot identified by pSnapshot. */ SQLITE_API int sqlite3_snapshot_open( sqlite3 *db, @@ -195706,6 +197217,7 @@ static int fts3IncrmergeLoad( for(i=nHeight; i>=0 && rc==SQLITE_OK; i--){ NodeReader reader; + memset(&reader, 0, sizeof(reader)); pNode = &pWriter->aNodeWriter[i]; if( pNode->block.a){ @@ -196576,7 +198088,7 @@ static u64 fts3ChecksumIndex( int rc; u64 cksum = 0; - assert( *pRc==SQLITE_OK ); + if( *pRc ) return 0; memset(&filter, 0, sizeof(filter)); memset(&csr, 0, sizeof(csr)); @@ -199752,25 +201264,51 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ ** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os). */ static const char jsonIsSpace[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; #define fast_isspace(x) (jsonIsSpace[(unsigned char)x]) +/* +** Characters that are special to JSON. Control charaters, +** '"' and '\\'. +*/ +static const char jsonIsOk[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}; + + #if !defined(SQLITE_DEBUG) && !defined(SQLITE_COVERAGE_TEST) # define VVA(X) #else @@ -199781,6 +201319,7 @@ static const char jsonIsSpace[] = { typedef struct JsonString JsonString; typedef struct JsonNode JsonNode; typedef struct JsonParse JsonParse; +typedef struct JsonCleanup JsonCleanup; /* An instance of this object represents a JSON string ** under construction. Really, this is a generic string accumulator @@ -199796,16 +201335,26 @@ struct JsonString { char zSpace[100]; /* Initial static space */ }; +/* A deferred cleanup task. A list of JsonCleanup objects might be +** run when the JsonParse object is destroyed. +*/ +struct JsonCleanup { + JsonCleanup *pJCNext; /* Next in a list */ + void (*xOp)(void*); /* Routine to run */ + void *pArg; /* Argument to xOp() */ +}; + /* JSON type values */ -#define JSON_NULL 0 -#define JSON_TRUE 1 -#define JSON_FALSE 2 -#define JSON_INT 3 -#define JSON_REAL 4 -#define JSON_STRING 5 -#define JSON_ARRAY 6 -#define JSON_OBJECT 7 +#define JSON_SUBST 0 /* Special edit node. Uses u.iPrev */ +#define JSON_NULL 1 +#define JSON_TRUE 2 +#define JSON_FALSE 3 +#define JSON_INT 4 +#define JSON_REAL 5 +#define JSON_STRING 6 +#define JSON_ARRAY 7 +#define JSON_OBJECT 8 /* The "subtype" set for JSON values */ #define JSON_SUBTYPE 74 /* Ascii for "J" */ @@ -199814,52 +201363,87 @@ struct JsonString { ** Names of the various JSON types: */ static const char * const jsonType[] = { + "subst", "null", "true", "false", "integer", "real", "text", "array", "object" }; /* Bit values for the JsonNode.jnFlag field */ -#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */ -#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */ -#define JNODE_REMOVE 0x04 /* Do not output */ -#define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */ -#define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */ -#define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */ -#define JNODE_LABEL 0x40 /* Is a label of an object */ -#define JNODE_JSON5 0x80 /* Node contains JSON5 enhancements */ +#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */ +#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */ +#define JNODE_REMOVE 0x04 /* Do not output */ +#define JNODE_REPLACE 0x08 /* Target of a JSON_SUBST node */ +#define JNODE_APPEND 0x10 /* More ARRAY/OBJECT entries at u.iAppend */ +#define JNODE_LABEL 0x20 /* Is a label of an object */ +#define JNODE_JSON5 0x40 /* Node contains JSON5 enhancements */ -/* A single node of parsed JSON +/* A single node of parsed JSON. An array of these nodes describes +** a parse of JSON + edits. +** +** Use the json_parse() SQL function (available when compiled with +** -DSQLITE_DEBUG) to see a dump of complete JsonParse objects, including +** a complete listing and decoding of the array of JsonNodes. */ struct JsonNode { u8 eType; /* One of the JSON_ type values */ u8 jnFlags; /* JNODE flags */ u8 eU; /* Which union element to use */ - u32 n; /* Bytes of content, or number of sub-nodes */ + u32 n; /* Bytes of content for INT, REAL or STRING + ** Number of sub-nodes for ARRAY and OBJECT + ** Node that SUBST applies to */ union { const char *zJContent; /* 1: Content for INT, REAL, and STRING */ u32 iAppend; /* 2: More terms for ARRAY and OBJECT */ u32 iKey; /* 3: Key for ARRAY objects in json_tree() */ - u32 iReplace; /* 4: Replacement content for JNODE_REPLACE */ - JsonNode *pPatch; /* 5: Node chain of patch for JNODE_PATCH */ + u32 iPrev; /* 4: Previous SUBST node, or 0 */ } u; }; -/* A completely parsed JSON string + +/* A parsed and possibly edited JSON string. Lifecycle: +** +** 1. JSON comes in and is parsed into an array aNode[]. The original +** JSON text is stored in zJson. +** +** 2. Zero or more changes are made (via json_remove() or json_replace() +** or similar) to the aNode[] array. +** +** 3. A new, edited and mimified JSON string is generated from aNode +** and stored in zAlt. The JsonParse object always owns zAlt. +** +** Step 1 always happens. Step 2 and 3 may or may not happen, depending +** on the operation. +** +** aNode[].u.zJContent entries typically point into zJson. Hence zJson +** must remain valid for the lifespan of the parse. For edits, +** aNode[].u.zJContent might point to malloced space other than zJson. +** Entries in pClup are responsible for freeing that extra malloced space. +** +** When walking the parse tree in aNode[], edits are ignored if useMod is +** false. */ struct JsonParse { u32 nNode; /* Number of slots of aNode[] used */ u32 nAlloc; /* Number of slots of aNode[] allocated */ JsonNode *aNode; /* Array of nodes containing the parse */ - const char *zJson; /* Original JSON string */ + char *zJson; /* Original JSON string (before edits) */ + char *zAlt; /* Revised and/or mimified JSON */ u32 *aUp; /* Index of parent of each node */ + JsonCleanup *pClup;/* Cleanup operations prior to freeing this object */ u16 iDepth; /* Nesting depth */ u8 nErr; /* Number of errors seen */ u8 oom; /* Set to true if out of memory */ + u8 bJsonIsRCStr; /* True if zJson is an RCStr */ u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ + u8 useMod; /* Actually use the edits contain inside aNode */ + u8 hasMod; /* aNode contains edits from the original zJson */ + u32 nJPRef; /* Number of references to this object */ int nJson; /* Length of the zJson string in bytes */ + int nAlt; /* Length of alternative JSON string zAlt, in bytes */ u32 iErr; /* Error location in zJson[] */ - u32 iHold; /* Replace cache line with the lowest iHold value */ + u32 iSubst; /* Last JSON_SUBST entry in aNode[] */ + u32 iHold; /* Age of this entry in the cache for LRU replacement */ }; /* @@ -199892,16 +201476,14 @@ static void jsonInit(JsonString *p, sqlite3_context *pCtx){ jsonZero(p); } - /* Free all allocated memory and reset the JsonString object back to its ** initial state. */ static void jsonReset(JsonString *p){ - if( !p->bStatic ) sqlite3_free(p->zBuf); + if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf); jsonZero(p); } - /* Report an out-of-memory (OOM) condition */ static void jsonOom(JsonString *p){ @@ -199918,7 +201500,7 @@ static int jsonGrow(JsonString *p, u32 N){ char *zNew; if( p->bStatic ){ if( p->bErr ) return 1; - zNew = sqlite3_malloc64(nTotal); + zNew = sqlite3RCStrNew(nTotal); if( zNew==0 ){ jsonOom(p); return SQLITE_NOMEM; @@ -199927,12 +201509,12 @@ static int jsonGrow(JsonString *p, u32 N){ p->zBuf = zNew; p->bStatic = 0; }else{ - zNew = sqlite3_realloc64(p->zBuf, nTotal); - if( zNew==0 ){ - jsonOom(p); + p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal); + if( p->zBuf==0 ){ + p->bErr = 1; + jsonZero(p); return SQLITE_NOMEM; } - p->zBuf = zNew; } p->nAlloc = nTotal; return SQLITE_OK; @@ -199940,12 +201522,35 @@ static int jsonGrow(JsonString *p, u32 N){ /* Append N bytes from zIn onto the end of the JsonString string. */ -static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ - if( N==0 ) return; - if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return; +static SQLITE_NOINLINE void jsonAppendExpand( + JsonString *p, + const char *zIn, + u32 N +){ + assert( N>0 ); + if( jsonGrow(p,N) ) return; memcpy(p->zBuf+p->nUsed, zIn, N); p->nUsed += N; } +static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ + if( N==0 ) return; + if( N+p->nUsed >= p->nAlloc ){ + jsonAppendExpand(p,zIn,N); + }else{ + memcpy(p->zBuf+p->nUsed, zIn, N); + p->nUsed += N; + } +} +static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){ + assert( N>0 ); + if( N+p->nUsed >= p->nAlloc ){ + jsonAppendExpand(p,zIn,N); + }else{ + memcpy(p->zBuf+p->nUsed, zIn, N); + p->nUsed += N; + } +} + /* Append formatted text (not to exceed N bytes) to the JsonString. */ @@ -199960,10 +201565,35 @@ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){ /* Append a single character */ -static void jsonAppendChar(JsonString *p, char c){ - if( p->nUsed>=p->nAlloc && jsonGrow(p,1)!=0 ) return; +static SQLITE_NOINLINE void jsonAppendCharExpand(JsonString *p, char c){ + if( jsonGrow(p,1) ) return; p->zBuf[p->nUsed++] = c; } +static void jsonAppendChar(JsonString *p, char c){ + if( p->nUsed>=p->nAlloc ){ + jsonAppendCharExpand(p,c); + }else{ + p->zBuf[p->nUsed++] = c; + } +} + +/* Try to force the string to be a zero-terminated RCStr string. +** +** Return true on success. Return false if an OOM prevents this +** from happening. +*/ +static int jsonForceRCStr(JsonString *p){ + jsonAppendChar(p, 0); + if( p->bErr ) return 0; + p->nUsed--; + if( p->bStatic==0 ) return 1; + p->nAlloc = 0; + p->nUsed++; + jsonGrow(p, p->nUsed); + p->nUsed--; + return p->bStatic==0; +} + /* Append a comma separator to the output buffer, if the previous ** character is not '[' or '{'. @@ -199972,7 +201602,8 @@ static void jsonAppendSeparator(JsonString *p){ char c; if( p->nUsed==0 ) return; c = p->zBuf[p->nUsed-1]; - if( c!='[' && c!='{' ) jsonAppendChar(p, ','); + if( c=='[' || c=='{' ) return; + jsonAppendChar(p, ','); } /* Append the N-byte string in zIn to the end of the JsonString string @@ -199986,11 +201617,16 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ p->zBuf[p->nUsed++] = '"'; for(i=0; izBuf[p->nUsed++] = c; + }else if( c=='"' || c=='\\' ){ json_simple_escape: if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return; p->zBuf[p->nUsed++] = '\\'; - }else if( c<=0x1f ){ + p->zBuf[p->nUsed++] = c; + }else if( c=='\'' ){ + p->zBuf[p->nUsed++] = c; + }else{ static const char aSpecial[] = { 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 @@ -200001,6 +201637,7 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ assert( aSpecial['\n']=='n' ); assert( aSpecial['\r']=='r' ); assert( aSpecial['\t']=='t' ); + assert( c>=0 && czBuf[p->nUsed++] = 'u'; p->zBuf[p->nUsed++] = '0'; p->zBuf[p->nUsed++] = '0'; - p->zBuf[p->nUsed++] = '0' + (c>>4); - c = "0123456789abcdef"[c&0xf]; + p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4]; + p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf]; } - p->zBuf[p->nUsed++] = c; } p->zBuf[p->nUsed++] = '"'; assert( p->nUsednAlloc ); @@ -200032,7 +201668,7 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ while( N>0 ){ for(i=0; i0 ){ - jsonAppendRaw(p, zIn, i); + jsonAppendRawNZ(p, zIn, i); zIn += i; N -= i; if( N==0 ) break; @@ -200043,16 +201679,16 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ jsonAppendChar(p, '\''); break; case 'v': - jsonAppendRaw(p, "\\u0009", 6); + jsonAppendRawNZ(p, "\\u0009", 6); break; case 'x': - jsonAppendRaw(p, "\\u00", 4); - jsonAppendRaw(p, &zIn[2], 2); + jsonAppendRawNZ(p, "\\u00", 4); + jsonAppendRawNZ(p, &zIn[2], 2); zIn += 2; N -= 2; break; case '0': - jsonAppendRaw(p, "\\u0000", 6); + jsonAppendRawNZ(p, "\\u0000", 6); break; case '\r': if( zIn[2]=='\n' ){ @@ -200070,7 +201706,7 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ N -= 2; break; default: - jsonAppendRaw(p, zIn, 2); + jsonAppendRawNZ(p, zIn, 2); break; } zIn += 2; @@ -200100,11 +201736,12 @@ static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ jsonPrintf(100,p,"%lld",i); }else{ assert( rc==2 ); - jsonAppendRaw(p, "9.0e999", 7); + jsonAppendRawNZ(p, "9.0e999", 7); } return; } - jsonAppendRaw(p, zIn, N); + assert( N>0 ); + jsonAppendRawNZ(p, zIn, N); } /* @@ -200136,7 +201773,7 @@ static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){ } } if( N>0 ){ - jsonAppendRaw(p, zIn, N); + jsonAppendRawNZ(p, zIn, N); } } @@ -200152,7 +201789,7 @@ static void jsonAppendValue( ){ switch( sqlite3_value_type(pValue) ){ case SQLITE_NULL: { - jsonAppendRaw(p, "null", 4); + jsonAppendRawNZ(p, "null", 4); break; } case SQLITE_FLOAT: { @@ -200188,15 +201825,25 @@ static void jsonAppendValue( /* Make the JSON in p the result of the SQL function. +** +** The JSON string is reset. */ static void jsonResult(JsonString *p){ if( p->bErr==0 ){ - sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, - p->bStatic ? SQLITE_TRANSIENT : sqlite3_free, - SQLITE_UTF8); - jsonZero(p); + if( p->bStatic ){ + sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, + SQLITE_TRANSIENT, SQLITE_UTF8); + }else if( jsonForceRCStr(p) ){ + sqlite3RCStrRef(p->zBuf); + sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, + (void(*)(void*))sqlite3RCStrUnref, + SQLITE_UTF8); + } } - assert( p->bStatic ); + if( p->bErr==1 ){ + sqlite3_result_error_nomem(p->pCtx); + } + jsonReset(p); } /************************************************************************** @@ -200221,20 +201868,73 @@ static u32 jsonNodeSize(JsonNode *pNode){ ** delete the JsonParse object itself. */ static void jsonParseReset(JsonParse *pParse){ - sqlite3_free(pParse->aNode); - pParse->aNode = 0; + while( pParse->pClup ){ + JsonCleanup *pTask = pParse->pClup; + pParse->pClup = pTask->pJCNext; + pTask->xOp(pTask->pArg); + sqlite3_free(pTask); + } + assert( pParse->nJPRef<=1 ); + if( pParse->aNode ){ + sqlite3_free(pParse->aNode); + pParse->aNode = 0; + } pParse->nNode = 0; pParse->nAlloc = 0; - sqlite3_free(pParse->aUp); - pParse->aUp = 0; + if( pParse->aUp ){ + sqlite3_free(pParse->aUp); + pParse->aUp = 0; + } + if( pParse->bJsonIsRCStr ){ + sqlite3RCStrUnref(pParse->zJson); + pParse->zJson = 0; + pParse->bJsonIsRCStr = 0; + } + if( pParse->zAlt ){ + sqlite3RCStrUnref(pParse->zAlt); + pParse->zAlt = 0; + } } /* ** Free a JsonParse object that was obtained from sqlite3_malloc(). +** +** Note that destroying JsonParse might call sqlite3RCStrUnref() to +** destroy the zJson value. The RCStr object might recursively invoke +** JsonParse to destroy this pParse object again. Take care to ensure +** that this recursive destructor sequence terminates harmlessly. */ static void jsonParseFree(JsonParse *pParse){ - jsonParseReset(pParse); - sqlite3_free(pParse); + if( pParse->nJPRef>1 ){ + pParse->nJPRef--; + }else{ + jsonParseReset(pParse); + sqlite3_free(pParse); + } +} + +/* +** Add a cleanup task to the JsonParse object. +** +** If an OOM occurs, the cleanup operation happens immediately +** and this function returns SQLITE_NOMEM. +*/ +static int jsonParseAddCleanup( + JsonParse *pParse, /* Add the cleanup task to this parser */ + void(*xOp)(void*), /* The cleanup task */ + void *pArg /* Argument to the cleanup */ +){ + JsonCleanup *pTask = sqlite3_malloc64( sizeof(*pTask) ); + if( pTask==0 ){ + pParse->oom = 1; + xOp(pArg); + return SQLITE_ERROR; + } + pTask->pJCNext = pParse->pClup; + pParse->pClup = pTask; + pTask->xOp = xOp; + pTask->pArg = pArg; + return SQLITE_OK; } /* @@ -200243,32 +201943,38 @@ static void jsonParseFree(JsonParse *pParse){ ** the number of JsonNode objects that are encoded. */ static void jsonRenderNode( + JsonParse *pParse, /* the complete parse of the JSON */ JsonNode *pNode, /* The node to render */ - JsonString *pOut, /* Write JSON here */ - sqlite3_value **aReplace /* Replacement values */ + JsonString *pOut /* Write JSON here */ ){ assert( pNode!=0 ); - if( pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH) ){ - if( (pNode->jnFlags & JNODE_REPLACE)!=0 && ALWAYS(aReplace!=0) ){ - assert( pNode->eU==4 ); - jsonAppendValue(pOut, aReplace[pNode->u.iReplace]); - return; + while( (pNode->jnFlags & JNODE_REPLACE)!=0 && pParse->useMod ){ + u32 idx = (u32)(pNode - pParse->aNode); + u32 i = pParse->iSubst; + while( 1 /*exit-by-break*/ ){ + assert( inNode ); + assert( pParse->aNode[i].eType==JSON_SUBST ); + assert( pParse->aNode[i].eU==4 ); + assert( pParse->aNode[i].u.iPrevaNode[i].n==idx ){ + pNode = &pParse->aNode[i+1]; + break; + } + i = pParse->aNode[i].u.iPrev; } - assert( pNode->eU==5 ); - pNode = pNode->u.pPatch; } switch( pNode->eType ){ default: { assert( pNode->eType==JSON_NULL ); - jsonAppendRaw(pOut, "null", 4); + jsonAppendRawNZ(pOut, "null", 4); break; } case JSON_TRUE: { - jsonAppendRaw(pOut, "true", 4); + jsonAppendRawNZ(pOut, "true", 4); break; } case JSON_FALSE: { - jsonAppendRaw(pOut, "false", 5); + jsonAppendRawNZ(pOut, "false", 5); break; } case JSON_STRING: { @@ -200284,7 +201990,8 @@ static void jsonRenderNode( }else if( pNode->jnFlags & JNODE_JSON5 ){ jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n); }else{ - jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); + assert( pNode->n>0 ); + jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); } break; } @@ -200293,7 +202000,8 @@ static void jsonRenderNode( if( pNode->jnFlags & JNODE_JSON5 ){ jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n); }else{ - jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); + assert( pNode->n>0 ); + jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); } break; } @@ -200302,7 +202010,8 @@ static void jsonRenderNode( if( pNode->jnFlags & JNODE_JSON5 ){ jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n); }else{ - jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); + assert( pNode->n>0 ); + jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); } break; } @@ -200311,15 +202020,16 @@ static void jsonRenderNode( jsonAppendChar(pOut, '['); for(;;){ while( j<=pNode->n ){ - if( (pNode[j].jnFlags & JNODE_REMOVE)==0 ){ + if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ jsonAppendSeparator(pOut); - jsonRenderNode(&pNode[j], pOut, aReplace); + jsonRenderNode(pParse, &pNode[j], pOut); } j += jsonNodeSize(&pNode[j]); } if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; assert( pNode->eU==2 ); - pNode = &pNode[pNode->u.iAppend]; + pNode = &pParse->aNode[pNode->u.iAppend]; j = 1; } jsonAppendChar(pOut, ']'); @@ -200330,17 +202040,18 @@ static void jsonRenderNode( jsonAppendChar(pOut, '{'); for(;;){ while( j<=pNode->n ){ - if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){ + if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ jsonAppendSeparator(pOut); - jsonRenderNode(&pNode[j], pOut, aReplace); + jsonRenderNode(pParse, &pNode[j], pOut); jsonAppendChar(pOut, ':'); - jsonRenderNode(&pNode[j+1], pOut, aReplace); + jsonRenderNode(pParse, &pNode[j+1], pOut); } j += 1 + jsonNodeSize(&pNode[j+1]); } if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; assert( pNode->eU==2 ); - pNode = &pNode[pNode->u.iAppend]; + pNode = &pParse->aNode[pNode->u.iAppend]; j = 1; } jsonAppendChar(pOut, '}'); @@ -200350,18 +202061,29 @@ static void jsonRenderNode( } /* -** Return a JsonNode and all its descendents as a JSON string. +** Return a JsonNode and all its descendants as a JSON string. */ static void jsonReturnJson( + JsonParse *pParse, /* The complete JSON */ JsonNode *pNode, /* Node to return */ sqlite3_context *pCtx, /* Return value for this function */ - sqlite3_value **aReplace /* Array of replacement values */ + int bGenerateAlt /* Also store the rendered text in zAlt */ ){ JsonString s; - jsonInit(&s, pCtx); - jsonRenderNode(pNode, &s, aReplace); - jsonResult(&s); - sqlite3_result_subtype(pCtx, JSON_SUBTYPE); + if( pParse->oom ){ + sqlite3_result_error_nomem(pCtx); + return; + } + if( pParse->nErr==0 ){ + jsonInit(&s, pCtx); + jsonRenderNode(pParse, pNode, &s); + if( bGenerateAlt && pParse->zAlt==0 && jsonForceRCStr(&s) ){ + pParse->zAlt = sqlite3RCStrRef(s.zBuf); + pParse->nAlt = s.nUsed; + } + jsonResult(&s); + sqlite3_result_subtype(pCtx, JSON_SUBTYPE); + } } /* @@ -200399,9 +202121,9 @@ static u32 jsonHexToInt4(const char *z){ ** Make the JsonNode the return value of the function. */ static void jsonReturn( + JsonParse *pParse, /* Complete JSON parse tree */ JsonNode *pNode, /* Node to return */ - sqlite3_context *pCtx, /* Return value for this function */ - sqlite3_value **aReplace /* Array of replacement values */ + sqlite3_context *pCtx /* Return value for this function */ ){ switch( pNode->eType ){ default: { @@ -200423,7 +202145,6 @@ static void jsonReturn( int bNeg = 0; const char *z; - assert( pNode->eU==1 ); z = pNode->u.zJContent; if( z[0]=='-' ){ z++; bNeg = 1; } @@ -200548,7 +202269,7 @@ static void jsonReturn( } case JSON_ARRAY: case JSON_OBJECT: { - jsonReturnJson(pNode, pCtx, aReplace); + jsonReturnJson(pParse, pNode, pCtx, 0); break; } } @@ -200570,6 +202291,12 @@ static int jsonParseAddNode(JsonParse*,u32,u32,const char*); #endif +/* +** Add a single node to pParse->aNode after first expanding the +** size of the aNode array. Return the index of the new node. +** +** If an OOM error occurs, set pParse->oom and return -1. +*/ static JSON_NOINLINE int jsonParseAddNodeExpand( JsonParse *pParse, /* Append the node to this object */ u32 eType, /* Node type */ @@ -200586,7 +202313,7 @@ static JSON_NOINLINE int jsonParseAddNodeExpand( pParse->oom = 1; return -1; } - pParse->nAlloc = nNew; + pParse->nAlloc = sqlite3_msize(pNew)/sizeof(JsonNode); pParse->aNode = pNew; assert( pParse->nNodenAlloc ); return jsonParseAddNode(pParse, eType, n, zContent); @@ -200604,10 +202331,13 @@ static int jsonParseAddNode( const char *zContent /* Content */ ){ JsonNode *p; - if( pParse->aNode==0 || pParse->nNode>=pParse->nAlloc ){ + assert( pParse->aNode!=0 || pParse->nNode>=pParse->nAlloc ); + if( pParse->nNode>=pParse->nAlloc ){ return jsonParseAddNodeExpand(pParse, eType, n, zContent); } + assert( pParse->aNode!=0 ); p = &pParse->aNode[pParse->nNode]; + assert( p!=0 ); p->eType = (u8)(eType & 0xff); p->jnFlags = (u8)(eType >> 8); VVA( p->eU = zContent ? 1 : 0 ); @@ -200616,6 +202346,52 @@ static int jsonParseAddNode( return pParse->nNode++; } +/* +** Add an array of new nodes to the current pParse->aNode array. +** Return the index of the first node added. +** +** If an OOM error occurs, set pParse->oom. +*/ +static void jsonParseAddNodeArray( + JsonParse *pParse, /* Append the node to this object */ + JsonNode *aNode, /* Array of nodes to add */ + u32 nNode /* Number of elements in aNew */ +){ + assert( aNode!=0 ); + assert( nNode>=1 ); + if( pParse->nNode + nNode > pParse->nAlloc ){ + u32 nNew = pParse->nNode + nNode; + JsonNode *aNew = sqlite3_realloc64(pParse->aNode, nNew*sizeof(JsonNode)); + if( aNew==0 ){ + pParse->oom = 1; + return; + } + pParse->nAlloc = sqlite3_msize(aNew)/sizeof(JsonNode); + pParse->aNode = aNew; + } + memcpy(&pParse->aNode[pParse->nNode], aNode, nNode*sizeof(JsonNode)); + pParse->nNode += nNode; +} + +/* +** Add a new JSON_SUBST node. The node immediately following +** this new node will be the substitute content for iNode. +*/ +static int jsonParseAddSubstNode( + JsonParse *pParse, /* Add the JSON_SUBST here */ + u32 iNode /* References this node */ +){ + int idx = jsonParseAddNode(pParse, JSON_SUBST, iNode, 0); + if( pParse->oom ) return -1; + pParse->aNode[iNode].jnFlags |= JNODE_REPLACE; + pParse->aNode[idx].eU = 4; + pParse->aNode[idx].u.iPrev = pParse->iSubst; + pParse->iSubst = idx; + pParse->hasMod = 1; + pParse->useMod = 1; + return idx; +} + /* ** Return true if z[] begins with 2 (or more) hexadecimal digits */ @@ -200782,7 +202558,7 @@ static const struct NanInfName { ** ** Special return values: ** -** 0 End if input +** 0 End of input ** -1 Syntax error ** -2 '}' seen ** -3 ']' seen @@ -200957,15 +202733,12 @@ json_parse_restart: jnFlags = 0; parse_string: cDelim = z[i]; - j = i+1; - for(;;){ + for(j=i+1; 1; j++){ + if( jsonIsOk[(unsigned char)z[j]] ) continue; c = z[j]; - if( (c & ~0x1f)==0 ){ - /* Control characters are not allowed in strings */ - pParse->iErr = j; - return -1; - } - if( c=='\\' ){ + if( c==cDelim ){ + break; + }else if( c=='\\' ){ c = z[++j]; if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f' || c=='n' || c=='r' || c=='t' @@ -200985,10 +202758,11 @@ json_parse_restart: pParse->iErr = j; return -1; } - }else if( c==cDelim ){ - break; + }else if( c<=0x1f ){ + /* Control characters are not allowed in strings */ + pParse->iErr = j; + return -1; } - j++; } jsonParseAddNode(pParse, JSON_STRING | (jnFlags<<8), j+1-i, &z[i]); return j+1; @@ -201224,20 +202998,18 @@ json_parse_restart: /* ** Parse a complete JSON string. Return 0 on success or non-zero if there -** are any errors. If an error occurs, free all memory associated with -** pParse. +** are any errors. If an error occurs, free all memory held by pParse, +** but not pParse itself. ** -** pParse is uninitialized when this routine is called. +** pParse must be initialized to an empty parse object prior to calling +** this routine. */ static int jsonParse( JsonParse *pParse, /* Initialize and fill this JsonParse object */ - sqlite3_context *pCtx, /* Report errors here */ - const char *zJson /* Input JSON text to be parsed */ + sqlite3_context *pCtx /* Report errors here */ ){ int i; - memset(pParse, 0, sizeof(*pParse)); - if( zJson==0 ) return 1; - pParse->zJson = zJson; + const char *zJson = pParse->zJson; i = jsonParseValue(pParse, 0); if( pParse->oom ) i = -1; if( i>0 ){ @@ -201266,6 +203038,7 @@ static int jsonParse( return 0; } + /* Mark node i of pParse as being a child of iParent. Call recursively ** to fill in all the descendants of node i. */ @@ -201315,35 +203088,49 @@ static int jsonParseFindParents(JsonParse *pParse){ #define JSON_CACHE_SZ 4 /* Max number of cache entries */ /* -** Obtain a complete parse of the JSON found in the first argument -** of the argv array. Use the sqlite3_get_auxdata() cache for this -** parse if it is available. If the cache is not available or if it -** is no longer valid, parse the JSON again and return the new parse, -** and also register the new parse so that it will be available for +** Obtain a complete parse of the JSON found in the pJson argument +** +** Use the sqlite3_get_auxdata() cache to find a preexisting parse +** if it is available. If the cache is not available or if it +** is no longer valid, parse the JSON again and return the new parse. +** Also register the new parse so that it will be available for ** future sqlite3_get_auxdata() calls. ** ** If an error occurs and pErrCtx!=0 then report the error on pErrCtx ** and return NULL. ** -** If an error occurs and pErrCtx==0 then return the Parse object with -** JsonParse.nErr non-zero. If the caller invokes this routine with -** pErrCtx==0 and it gets back a JsonParse with nErr!=0, then the caller -** is responsible for invoking jsonParseFree() on the returned value. -** But the caller may invoke jsonParseFree() *only* if pParse->nErr!=0. +** The returned pointer (if it is not NULL) is owned by the cache in +** most cases, not the caller. The caller does NOT need to invoke +** jsonParseFree(), in most cases. +** +** Except, if an error occurs and pErrCtx==0 then return the JsonParse +** object with JsonParse.nErr non-zero and the caller will own the JsonParse +** object. In that case, it will be the responsibility of the caller to +** invoke jsonParseFree(). To summarize: +** +** pErrCtx!=0 || p->nErr==0 ==> Return value p is owned by the +** cache. Call does not need to +** free it. +** +** pErrCtx==0 && p->nErr!=0 ==> Return value is owned by the caller +** and so the caller must free it. */ static JsonParse *jsonParseCached( - sqlite3_context *pCtx, - sqlite3_value **argv, - sqlite3_context *pErrCtx + sqlite3_context *pCtx, /* Context to use for cache search */ + sqlite3_value *pJson, /* Function param containing JSON text */ + sqlite3_context *pErrCtx, /* Write parse errors here if not NULL */ + int bUnedited /* No prior edits allowed */ ){ - const char *zJson = (const char*)sqlite3_value_text(argv[0]); - int nJson = sqlite3_value_bytes(argv[0]); + char *zJson = (char*)sqlite3_value_text(pJson); + int nJson = sqlite3_value_bytes(pJson); JsonParse *p; JsonParse *pMatch = 0; int iKey; int iMinKey = 0; u32 iMinHold = 0xffffffff; u32 iMaxHold = 0; + int bJsonRCStr; + if( zJson==0 ) return 0; for(iKey=0; iKeynJson==nJson - && memcmp(p->zJson,zJson,nJson)==0 + && (p->hasMod==0 || bUnedited==0) + && (p->zJson==zJson || memcmp(p->zJson,zJson,nJson)==0) ){ p->nErr = 0; + p->useMod = 0; + pMatch = p; + }else + if( pMatch==0 + && p->zAlt!=0 + && bUnedited==0 + && p->nAlt==nJson + && memcmp(p->zAlt, zJson, nJson)==0 + ){ + p->nErr = 0; + p->useMod = 1; pMatch = p; }else if( p->iHoldiHold; @@ -201366,28 +203165,44 @@ static JsonParse *jsonParseCached( } } if( pMatch ){ + /* The input JSON text was found in the cache. Use the preexisting + ** parse of this JSON */ pMatch->nErr = 0; pMatch->iHold = iMaxHold+1; + assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */ return pMatch; } - p = sqlite3_malloc64( sizeof(*p) + nJson + 1 ); + + /* The input JSON was not found anywhere in the cache. We will need + ** to parse it ourselves and generate a new JsonParse object. + */ + bJsonRCStr = sqlite3ValueIsOfClass(pJson,(void(*)(void*))sqlite3RCStrUnref); + p = sqlite3_malloc64( sizeof(*p) + (bJsonRCStr ? 0 : nJson+1) ); if( p==0 ){ sqlite3_result_error_nomem(pCtx); return 0; } memset(p, 0, sizeof(*p)); - p->zJson = (char*)&p[1]; - memcpy((char*)p->zJson, zJson, nJson+1); - if( jsonParse(p, pErrCtx, p->zJson) ){ + if( bJsonRCStr ){ + p->zJson = sqlite3RCStrRef(zJson); + p->bJsonIsRCStr = 1; + }else{ + p->zJson = (char*)&p[1]; + memcpy(p->zJson, zJson, nJson+1); + } + p->nJPRef = 1; + if( jsonParse(p, pErrCtx) ){ if( pErrCtx==0 ){ p->nErr = 1; + assert( p->nJPRef==1 ); /* Caller will own the new JsonParse object p */ return p; } - sqlite3_free(p); + jsonParseFree(p); return 0; } p->nJson = nJson; p->iHold = iMaxHold+1; + /* Transfer ownership of the new JsonParse to the cache */ sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p, (void(*)(void*))jsonParseFree); return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey); @@ -201438,9 +203253,31 @@ static JsonNode *jsonLookupStep( ){ u32 i, j, nKey; const char *zKey; - JsonNode *pRoot = &pParse->aNode[iRoot]; + JsonNode *pRoot; + if( pParse->oom ) return 0; + pRoot = &pParse->aNode[iRoot]; + if( pRoot->jnFlags & (JNODE_REPLACE|JNODE_REMOVE) && pParse->useMod ){ + while( (pRoot->jnFlags & JNODE_REPLACE)!=0 ){ + u32 idx = (u32)(pRoot - pParse->aNode); + i = pParse->iSubst; + while( 1 /*exit-by-break*/ ){ + assert( inNode ); + assert( pParse->aNode[i].eType==JSON_SUBST ); + assert( pParse->aNode[i].eU==4 ); + assert( pParse->aNode[i].u.iPrevaNode[i].n==idx ){ + pRoot = &pParse->aNode[i+1]; + iRoot = i+1; + break; + } + i = pParse->aNode[i].u.iPrev; + } + } + if( pRoot->jnFlags & JNODE_REMOVE ){ + return 0; + } + } if( zPath[0]==0 ) return pRoot; - if( pRoot->jnFlags & JNODE_REPLACE ) return 0; if( zPath[0]=='.' ){ if( pRoot->eType!=JSON_OBJECT ) return 0; zPath++; @@ -201474,14 +203311,16 @@ static JsonNode *jsonLookupStep( j += jsonNodeSize(&pRoot[j]); } if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; assert( pRoot->eU==2 ); - iRoot += pRoot->u.iAppend; + iRoot = pRoot->u.iAppend; pRoot = &pParse->aNode[iRoot]; j = 1; } if( pApnd ){ u32 iStart, iLabel; JsonNode *pNode; + assert( pParse->useMod ); iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); zPath += i; @@ -201490,7 +203329,7 @@ static JsonNode *jsonLookupStep( if( pNode ){ pRoot = &pParse->aNode[iRoot]; assert( pRoot->eU==0 ); - pRoot->u.iAppend = iStart - iRoot; + pRoot->u.iAppend = iStart; pRoot->jnFlags |= JNODE_APPEND; VVA( pRoot->eU = 2 ); pParse->aNode[iLabel].jnFlags |= JNODE_RAW; @@ -201511,12 +203350,13 @@ static JsonNode *jsonLookupStep( if( pRoot->eType!=JSON_ARRAY ) return 0; for(;;){ while( j<=pBase->n ){ - if( (pBase[j].jnFlags & JNODE_REMOVE)==0 ) i++; + if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++; j += jsonNodeSize(&pBase[j]); } if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; assert( pBase->eU==2 ); - iBase += pBase->u.iAppend; + iBase = pBase->u.iAppend; pBase = &pParse->aNode[iBase]; j = 1; } @@ -201544,13 +203384,16 @@ static JsonNode *jsonLookupStep( zPath += j + 1; j = 1; for(;;){ - while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & JNODE_REMOVE)!=0) ){ - if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 ) i--; + while( j<=pRoot->n + && (i>0 || ((pRoot[j].jnFlags & JNODE_REMOVE)!=0 && pParse->useMod)) + ){ + if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i--; j += jsonNodeSize(&pRoot[j]); } if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; assert( pRoot->eU==2 ); - iRoot += pRoot->u.iAppend; + iRoot = pRoot->u.iAppend; pRoot = &pParse->aNode[iRoot]; j = 1; } @@ -201560,13 +203403,14 @@ static JsonNode *jsonLookupStep( if( i==0 && pApnd ){ u32 iStart; JsonNode *pNode; + assert( pParse->useMod ); iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0); pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); if( pParse->oom ) return 0; if( pNode ){ pRoot = &pParse->aNode[iRoot]; assert( pRoot->eU==0 ); - pRoot->u.iAppend = iStart - iRoot; + pRoot->u.iAppend = iStart; pRoot->jnFlags |= JNODE_APPEND; VVA( pRoot->eU = 2 ); } @@ -201693,47 +203537,90 @@ static void jsonRemoveAllNulls(JsonNode *pNode){ ** SQL functions used for testing and debugging ****************************************************************************/ +#if SQLITE_DEBUG +/* +** Print N node entries. +*/ +static void jsonDebugPrintNodeEntries( + JsonNode *aNode, /* First node entry to print */ + int N /* Number of node entries to print */ +){ + int i; + for(i=0; iaNode, p->nNode); +} +static void jsonDebugPrintNode(JsonNode *pNode){ + jsonDebugPrintNodeEntries(pNode, jsonNodeSize(pNode)); +} +#else + /* The usual case */ +# define jsonDebugPrintNode(X) +# define jsonDebugPrintParse(X) +#endif + #ifdef SQLITE_DEBUG /* -** The json_parse(JSON) function returns a string which describes -** a parse of the JSON provided. Or it returns NULL if JSON is not -** well-formed. +** SQL function: json_parse(JSON) +** +** Parse JSON using jsonParseCached(). Then print a dump of that +** parse on standard output. Return the mimified JSON result, just +** like the json() function. */ static void jsonParseFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ - JsonString s; /* Output string - not real JSON */ - JsonParse x; /* The parse */ - u32 i; + JsonParse *p; /* The parse */ assert( argc==1 ); - if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; - jsonParseFindParents(&x); - jsonInit(&s, ctx); - for(i=0; inNode); + printf("nAlloc = %u\n", p->nAlloc); + printf("nJson = %d\n", p->nJson); + printf("nAlt = %d\n", p->nAlt); + printf("nErr = %u\n", p->nErr); + printf("oom = %u\n", p->oom); + printf("hasNonstd = %u\n", p->hasNonstd); + printf("useMod = %u\n", p->useMod); + printf("hasMod = %u\n", p->hasMod); + printf("nJPRef = %u\n", p->nJPRef); + printf("iSubst = %u\n", p->iSubst); + printf("iHold = %u\n", p->iHold); + jsonDebugPrintNodeEntries(p->aNode, p->nNode); + jsonReturnJson(p, p->aNode, ctx, 1); } /* @@ -201817,7 +203704,7 @@ static void jsonArrayLengthFunc( u32 i; JsonNode *pNode; - p = jsonParseCached(ctx, argv, ctx); + p = jsonParseCached(ctx, argv[0], ctx, 0); if( p==0 ) return; assert( p->nNode ); if( argc==2 ){ @@ -201830,9 +203717,16 @@ static void jsonArrayLengthFunc( return; } if( pNode->eType==JSON_ARRAY ){ - assert( (pNode->jnFlags & JNODE_APPEND)==0 ); - for(i=1; i<=pNode->n; n++){ - i += jsonNodeSize(&pNode[i]); + while( 1 /*exit-by-break*/ ){ + i = 1; + while( i<=pNode->n ){ + if( (pNode[i].jnFlags & JNODE_REMOVE)==0 ) n++; + i += jsonNodeSize(&pNode[i]); + } + if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + if( p->useMod==0 ) break; + assert( pNode->eU==2 ); + pNode = &p->aNode[pNode->u.iAppend]; } } sqlite3_result_int64(ctx, n); @@ -201879,7 +203773,7 @@ static void jsonExtractFunc( JsonString jx; if( argc<2 ) return; - p = jsonParseCached(ctx, argv, ctx); + p = jsonParseCached(ctx, argv[0], ctx, 0); if( p==0 ) return; if( argc==2 ){ /* With a single PATH argument */ @@ -201897,11 +203791,11 @@ static void jsonExtractFunc( */ jsonInit(&jx, ctx); if( sqlite3Isdigit(zPath[0]) ){ - jsonAppendRaw(&jx, "$[", 2); + jsonAppendRawNZ(&jx, "$[", 2); jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); - jsonAppendRaw(&jx, "]", 2); + jsonAppendRawNZ(&jx, "]", 2); }else{ - jsonAppendRaw(&jx, "$.", 1 + (zPath[0]!='[')); + jsonAppendRawNZ(&jx, "$.", 1 + (zPath[0]!='[')); jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); jsonAppendChar(&jx, 0); } @@ -201912,15 +203806,15 @@ static void jsonExtractFunc( } if( pNode ){ if( flags & JSON_JSON ){ - jsonReturnJson(pNode, ctx, 0); + jsonReturnJson(p, pNode, ctx, 0); }else{ - jsonReturn(pNode, ctx, 0); + jsonReturn(p, pNode, ctx); sqlite3_result_subtype(ctx, 0); } } }else{ pNode = jsonLookup(p, zPath, 0, ctx); - if( p->nErr==0 && pNode ) jsonReturn(pNode, ctx, 0); + if( p->nErr==0 && pNode ) jsonReturn(p, pNode, ctx); } }else{ /* Two or more PATH arguments results in a JSON array with each @@ -201934,9 +203828,9 @@ static void jsonExtractFunc( if( p->nErr ) break; jsonAppendSeparator(&jx); if( pNode ){ - jsonRenderNode(pNode, &jx, 0); + jsonRenderNode(p, pNode, &jx); }else{ - jsonAppendRaw(&jx, "null", 4); + jsonAppendRawNZ(&jx, "null", 4); } } if( i==argc ){ @@ -201981,45 +203875,38 @@ static JsonNode *jsonMergePatch( assert( pTarget[j].eType==JSON_STRING ); assert( pTarget[j].jnFlags & JNODE_LABEL ); if( jsonSameLabel(&pPatch[i], &pTarget[j]) ){ - if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break; + if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ) break; if( pPatch[i+1].eType==JSON_NULL ){ pTarget[j+1].jnFlags |= JNODE_REMOVE; }else{ JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]); if( pNew==0 ) return 0; - pTarget = &pParse->aNode[iTarget]; - if( pNew!=&pTarget[j+1] ){ - assert( pTarget[j+1].eU==0 - || pTarget[j+1].eU==1 - || pTarget[j+1].eU==2 ); - testcase( pTarget[j+1].eU==1 ); - testcase( pTarget[j+1].eU==2 ); - VVA( pTarget[j+1].eU = 5 ); - pTarget[j+1].u.pPatch = pNew; - pTarget[j+1].jnFlags |= JNODE_PATCH; + if( pNew!=&pParse->aNode[iTarget+j+1] ){ + jsonParseAddSubstNode(pParse, iTarget+j+1); + jsonParseAddNodeArray(pParse, pNew, jsonNodeSize(pNew)); } + pTarget = &pParse->aNode[iTarget]; } break; } } if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){ - int iStart, iPatch; - iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); + int iStart; + JsonNode *pApnd; + u32 nApnd; + iStart = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); - iPatch = jsonParseAddNode(pParse, JSON_TRUE, 0, 0); + pApnd = &pPatch[i+1]; + if( pApnd->eType==JSON_OBJECT ) jsonRemoveAllNulls(pApnd); + nApnd = jsonNodeSize(pApnd); + jsonParseAddNodeArray(pParse, pApnd, jsonNodeSize(pApnd)); if( pParse->oom ) return 0; - jsonRemoveAllNulls(pPatch); - pTarget = &pParse->aNode[iTarget]; - assert( pParse->aNode[iRoot].eU==0 || pParse->aNode[iRoot].eU==2 ); - testcase( pParse->aNode[iRoot].eU==2 ); + pParse->aNode[iStart].n = 1+nApnd; pParse->aNode[iRoot].jnFlags |= JNODE_APPEND; + pParse->aNode[iRoot].u.iAppend = iStart; VVA( pParse->aNode[iRoot].eU = 2 ); - pParse->aNode[iRoot].u.iAppend = iStart - iRoot; iRoot = iStart; - assert( pParse->aNode[iPatch].eU==0 ); - VVA( pParse->aNode[iPatch].eU = 5 ); - pParse->aNode[iPatch].jnFlags |= JNODE_PATCH; - pParse->aNode[iPatch].u.pPatch = &pPatch[i+1]; + pTarget = &pParse->aNode[iTarget]; } } return pTarget; @@ -202035,25 +203922,28 @@ static void jsonPatchFunc( int argc, sqlite3_value **argv ){ - JsonParse x; /* The JSON that is being patched */ - JsonParse y; /* The patch */ + JsonParse *pX; /* The JSON that is being patched */ + JsonParse *pY; /* The patch */ JsonNode *pResult; /* The result of the merge */ UNUSED_PARAMETER(argc); - if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; - if( jsonParse(&y, ctx, (const char*)sqlite3_value_text(argv[1])) ){ - jsonParseReset(&x); - return; - } - pResult = jsonMergePatch(&x, 0, y.aNode); - assert( pResult!=0 || x.oom ); - if( pResult ){ - jsonReturnJson(pResult, ctx, 0); + pX = jsonParseCached(ctx, argv[0], ctx, 1); + if( pX==0 ) return; + assert( pX->hasMod==0 ); + pX->hasMod = 1; + pY = jsonParseCached(ctx, argv[1], ctx, 1); + if( pY==0 ) return; + pX->useMod = 1; + pY->useMod = 1; + pResult = jsonMergePatch(pX, 0, pY->aNode); + assert( pResult!=0 || pX->oom ); + if( pResult && pX->oom==0 ){ + jsonDebugPrintParse(pX); + jsonDebugPrintNode(pResult); + jsonReturnJson(pX, pResult, ctx, 0); }else{ sqlite3_result_error_nomem(ctx); } - jsonParseReset(&x); - jsonParseReset(&y); } @@ -202109,26 +203999,118 @@ static void jsonRemoveFunc( int argc, sqlite3_value **argv ){ - JsonParse x; /* The parse */ + JsonParse *pParse; /* The parse */ JsonNode *pNode; const char *zPath; u32 i; if( argc<1 ) return; - if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; - assert( x.nNode ); + pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); + if( pParse==0 ) return; for(i=1; i<(u32)argc; i++){ zPath = (const char*)sqlite3_value_text(argv[i]); if( zPath==0 ) goto remove_done; - pNode = jsonLookup(&x, zPath, 0, ctx); - if( x.nErr ) goto remove_done; - if( pNode ) pNode->jnFlags |= JNODE_REMOVE; + pNode = jsonLookup(pParse, zPath, 0, ctx); + if( pParse->nErr ) goto remove_done; + if( pNode ){ + pNode->jnFlags |= JNODE_REMOVE; + pParse->hasMod = 1; + pParse->useMod = 1; + } } - if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){ - jsonReturnJson(x.aNode, ctx, 0); + if( (pParse->aNode[0].jnFlags & JNODE_REMOVE)==0 ){ + jsonReturnJson(pParse, pParse->aNode, ctx, 1); } remove_done: - jsonParseReset(&x); + jsonDebugPrintParse(p); +} + +/* +** Substitute the value at iNode with the pValue parameter. +*/ +static void jsonReplaceNode( + sqlite3_context *pCtx, + JsonParse *p, + int iNode, + sqlite3_value *pValue +){ + int idx = jsonParseAddSubstNode(p, iNode); + if( idx<=0 ){ + assert( p->oom ); + return; + } + switch( sqlite3_value_type(pValue) ){ + case SQLITE_NULL: { + jsonParseAddNode(p, JSON_NULL, 0, 0); + break; + } + case SQLITE_FLOAT: { + char *z = sqlite3_mprintf("%!0.15g", sqlite3_value_double(pValue)); + int n; + if( z==0 ){ + p->oom = 1; + break; + } + n = sqlite3Strlen30(z); + jsonParseAddNode(p, JSON_REAL, n, z); + jsonParseAddCleanup(p, sqlite3_free, z); + break; + } + case SQLITE_INTEGER: { + char *z = sqlite3_mprintf("%lld", sqlite3_value_int64(pValue)); + int n; + if( z==0 ){ + p->oom = 1; + break; + } + n = sqlite3Strlen30(z); + jsonParseAddNode(p, JSON_INT, n, z); + jsonParseAddCleanup(p, sqlite3_free, z); + + break; + } + case SQLITE_TEXT: { + const char *z = (const char*)sqlite3_value_text(pValue); + u32 n = (u32)sqlite3_value_bytes(pValue); + if( z==0 ){ + p->oom = 1; + break; + } + if( sqlite3_value_subtype(pValue)!=JSON_SUBTYPE ){ + char *zCopy = sqlite3DbStrDup(0, z); + int k; + if( zCopy ){ + jsonParseAddCleanup(p, sqlite3_free, zCopy); + }else{ + p->oom = 1; + sqlite3_result_error_nomem(pCtx); + } + k = jsonParseAddNode(p, JSON_STRING, n, zCopy); + assert( k>0 || p->oom ); + if( p->oom==0 ) p->aNode[k].jnFlags |= JNODE_RAW; + }else{ + JsonParse *pPatch = jsonParseCached(pCtx, pValue, pCtx, 1); + if( pPatch==0 ){ + p->oom = 1; + break; + } + jsonParseAddNodeArray(p, pPatch->aNode, pPatch->nNode); + /* The nodes copied out of pPatch and into p likely contain + ** u.zJContent pointers into pPatch->zJson. So preserve the + ** content of pPatch until p is destroyed. */ + assert( pPatch->nJPRef>=1 ); + pPatch->nJPRef++; + jsonParseAddCleanup(p, (void(*)(void*))jsonParseFree, pPatch); + } + break; + } + default: { + jsonParseAddNode(p, JSON_NULL, 0, 0); + sqlite3_result_error(pCtx, "JSON cannot hold BLOB values", -1); + p->nErr++; + break; + } + } } /* @@ -202142,7 +204124,7 @@ static void jsonReplaceFunc( int argc, sqlite3_value **argv ){ - JsonParse x; /* The parse */ + JsonParse *pParse; /* The parse */ JsonNode *pNode; const char *zPath; u32 i; @@ -202152,28 +204134,20 @@ static void jsonReplaceFunc( jsonWrongNumArgs(ctx, "replace"); return; } - if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; - assert( x.nNode ); + pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); + if( pParse==0 ) return; for(i=1; i<(u32)argc; i+=2){ zPath = (const char*)sqlite3_value_text(argv[i]); - pNode = jsonLookup(&x, zPath, 0, ctx); - if( x.nErr ) goto replace_err; + pParse->useMod = 1; + pNode = jsonLookup(pParse, zPath, 0, ctx); + if( pParse->nErr ) goto replace_err; if( pNode ){ - assert( pNode->eU==0 || pNode->eU==1 || pNode->eU==4 ); - testcase( pNode->eU!=0 && pNode->eU!=1 ); - pNode->jnFlags |= (u8)JNODE_REPLACE; - VVA( pNode->eU = 4 ); - pNode->u.iReplace = i + 1; + jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]); } } - if( x.aNode[0].jnFlags & JNODE_REPLACE ){ - assert( x.aNode[0].eU==4 ); - sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); - }else{ - jsonReturnJson(x.aNode, ctx, argv); - } + jsonReturnJson(pParse, pParse->aNode, ctx, 1); replace_err: - jsonParseReset(&x); + jsonDebugPrintParse(pParse); } @@ -202194,7 +204168,7 @@ static void jsonSetFunc( int argc, sqlite3_value **argv ){ - JsonParse x; /* The parse */ + JsonParse *pParse; /* The parse */ JsonNode *pNode; const char *zPath; u32 i; @@ -202206,33 +204180,27 @@ static void jsonSetFunc( jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert"); return; } - if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; - assert( x.nNode ); + pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); + if( pParse==0 ) return; for(i=1; i<(u32)argc; i+=2){ zPath = (const char*)sqlite3_value_text(argv[i]); bApnd = 0; - pNode = jsonLookup(&x, zPath, &bApnd, ctx); - if( x.oom ){ + pParse->useMod = 1; + pNode = jsonLookup(pParse, zPath, &bApnd, ctx); + if( pParse->oom ){ sqlite3_result_error_nomem(ctx); goto jsonSetDone; - }else if( x.nErr ){ + }else if( pParse->nErr ){ goto jsonSetDone; }else if( pNode && (bApnd || bIsSet) ){ - testcase( pNode->eU!=0 && pNode->eU!=1 ); - assert( pNode->eU!=3 && pNode->eU!=5 ); - VVA( pNode->eU = 4 ); - pNode->jnFlags |= (u8)JNODE_REPLACE; - pNode->u.iReplace = i + 1; + jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]); } } - if( x.aNode[0].jnFlags & JNODE_REPLACE ){ - assert( x.aNode[0].eU==4 ); - sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); - }else{ - jsonReturnJson(x.aNode, ctx, argv); - } + jsonDebugPrintParse(pParse); + jsonReturnJson(pParse, pParse->aNode, ctx, 1); + jsonSetDone: - jsonParseReset(&x); + /* no cleanup required */; } /* @@ -202251,7 +204219,7 @@ static void jsonTypeFunc( const char *zPath; JsonNode *pNode; - p = jsonParseCached(ctx, argv, ctx); + p = jsonParseCached(ctx, argv[0], ctx, 0); if( p==0 ) return; if( argc==2 ){ zPath = (const char*)sqlite3_value_text(argv[1]); @@ -202277,13 +204245,19 @@ static void jsonValidFunc( ){ JsonParse *p; /* The parse */ UNUSED_PARAMETER(argc); - if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - p = jsonParseCached(ctx, argv, 0); + if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ +#ifdef SQLITE_LEGACY_JSON_VALID + /* Incorrect legacy behavior was to return FALSE for a NULL input */ + sqlite3_result_int(ctx, 0); +#endif + return; + } + p = jsonParseCached(ctx, argv[0], 0, 0); if( p==0 || p->oom ){ sqlite3_result_error_nomem(ctx); sqlite3_free(p); }else{ - sqlite3_result_int(ctx, p->nErr==0 && p->hasNonstd==0); + sqlite3_result_int(ctx, p->nErr==0 && (p->hasNonstd==0 || p->useMod)); if( p->nErr ) jsonParseFree(p); } } @@ -202324,7 +204298,7 @@ static void jsonErrorFunc( JsonParse *p; /* The parse */ UNUSED_PARAMETER(argc); if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - p = jsonParseCached(ctx, argv, 0); + p = jsonParseCached(ctx, argv[0], 0, 0); if( p==0 || p->oom ){ sqlite3_result_error_nomem(ctx); sqlite3_free(p); @@ -202333,7 +204307,7 @@ static void jsonErrorFunc( }else{ int n = 1; u32 i; - const char *z = p->zJson; + const char *z = (const char*)sqlite3_value_text(argv[0]); for(i=0; iiErr && ALWAYS(z[i]); i++){ if( (z[i]&0xc0)!=0x80 ) n++; } @@ -202381,7 +204355,8 @@ static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ assert( pStr->bStatic ); }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, - pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free); + pStr->bStatic ? SQLITE_TRANSIENT : + (void(*)(void*))sqlite3RCStrUnref); pStr->bStatic = 1; }else{ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); @@ -202422,7 +204397,7 @@ static void jsonGroupInverse( pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); #ifdef NEVER /* pStr is always non-NULL since jsonArrayStep() or jsonObjectStep() will - ** always have been called to initalize it */ + ** always have been called to initialize it */ if( NEVER(!pStr) ) return; #endif z = pStr->zBuf; @@ -202489,7 +204464,8 @@ static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ assert( pStr->bStatic ); }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, - pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free); + pStr->bStatic ? SQLITE_TRANSIENT : + (void(*)(void*))sqlite3RCStrUnref); pStr->bStatic = 1; }else{ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); @@ -202600,7 +204576,6 @@ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ /* Reset a JsonEachCursor back to its original state. Free any memory ** held. */ static void jsonEachCursorReset(JsonEachCursor *p){ - sqlite3_free(p->zJson); sqlite3_free(p->zRoot); jsonParseReset(&p->sParse); p->iRowid = 0; @@ -202738,7 +204713,7 @@ static int jsonEachColumn( case JEACH_KEY: { if( p->i==0 ) break; if( p->eType==JSON_OBJECT ){ - jsonReturn(pThis, ctx, 0); + jsonReturn(&p->sParse, pThis, ctx); }else if( p->eType==JSON_ARRAY ){ u32 iKey; if( p->bRecursive ){ @@ -202754,7 +204729,7 @@ static int jsonEachColumn( } case JEACH_VALUE: { if( pThis->jnFlags & JNODE_LABEL ) pThis++; - jsonReturn(pThis, ctx, 0); + jsonReturn(&p->sParse, pThis, ctx); break; } case JEACH_TYPE: { @@ -202765,7 +204740,7 @@ static int jsonEachColumn( case JEACH_ATOM: { if( pThis->jnFlags & JNODE_LABEL ) pThis++; if( pThis->eType>=JSON_ARRAY ) break; - jsonReturn(pThis, ctx, 0); + jsonReturn(&p->sParse, pThis, ctx); break; } case JEACH_ID: { @@ -202920,11 +204895,19 @@ static int jsonEachFilter( if( idxNum==0 ) return SQLITE_OK; z = (const char*)sqlite3_value_text(argv[0]); if( z==0 ) return SQLITE_OK; - n = sqlite3_value_bytes(argv[0]); - p->zJson = sqlite3_malloc64( n+1 ); - if( p->zJson==0 ) return SQLITE_NOMEM; - memcpy(p->zJson, z, (size_t)n+1); - if( jsonParse(&p->sParse, 0, p->zJson) ){ + memset(&p->sParse, 0, sizeof(p->sParse)); + p->sParse.nJPRef = 1; + if( sqlite3ValueIsOfClass(argv[0], (void(*)(void*))sqlite3RCStrUnref) ){ + p->sParse.zJson = sqlite3RCStrRef((char*)z); + }else{ + n = sqlite3_value_bytes(argv[0]); + p->sParse.zJson = sqlite3RCStrNew( n+1 ); + if( p->sParse.zJson==0 ) return SQLITE_NOMEM; + memcpy(p->sParse.zJson, z, (size_t)n+1); + } + p->sParse.bJsonIsRCStr = 1; + p->zJson = p->sParse.zJson; + if( jsonParse(&p->sParse, 0) ){ int rc = SQLITE_NOMEM; if( p->sParse.oom==0 ){ sqlite3_free(cur->pVtab->zErrMsg); @@ -203202,6 +205185,11 @@ typedef unsigned int u32; #endif #endif /* !defined(SQLITE_AMALGAMATION) */ +/* Macro to check for 4-byte alignment. Only used inside of assert() */ +#ifdef SQLITE_DEBUG +# define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0) +#endif + /* #include */ /* #include */ /* #include */ @@ -203608,7 +205596,7 @@ static int readInt16(u8 *p){ return (p[0]<<8) + p[1]; } static void readCoord(u8 *p, RtreeCoord *pCoord){ - assert( (((sqlite3_uint64)p)&3)==0 ); /* p is always 4-byte aligned */ + assert( FOUR_BYTE_ALIGNED(p) ); #if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 pCoord->u = _byteswap_ulong(*(u32*)p); #elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 @@ -203662,7 +205650,7 @@ static void writeInt16(u8 *p, int i){ } static int writeCoord(u8 *p, RtreeCoord *pCoord){ u32 i; - assert( (((sqlite3_uint64)p)&3)==0 ); /* p is always 4-byte aligned */ + assert( FOUR_BYTE_ALIGNED(p) ); assert( sizeof(RtreeCoord)==4 ); assert( sizeof(u32)==4 ); #if SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 @@ -204390,7 +206378,7 @@ static void rtreeNonleafConstraint( assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE || p->op==RTREE_FALSE ); - assert( (((sqlite3_uint64)pCellData)&3)==0 ); /* 4-byte aligned */ + assert( FOUR_BYTE_ALIGNED(pCellData) ); switch( p->op ){ case RTREE_TRUE: return; /* Always satisfied */ case RTREE_FALSE: break; /* Never satisfied */ @@ -204443,7 +206431,7 @@ static void rtreeLeafConstraint( || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE || p->op==RTREE_FALSE ); pCellData += 8 + p->iCoord*4; - assert( (((sqlite3_uint64)pCellData)&3)==0 ); /* 4-byte aligned */ + assert( FOUR_BYTE_ALIGNED(pCellData) ); RTREE_DECODE_COORD(eInt, pCellData, xN); switch( p->op ){ case RTREE_TRUE: return; /* Always satisfied */ @@ -205013,7 +207001,20 @@ static int rtreeFilter( p->pInfo->nCoord = pRtree->nDim2; p->pInfo->anQueue = pCsr->anQueue; p->pInfo->mxLevel = pRtree->iDepth + 1; - }else if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ + }else if( eType==SQLITE_INTEGER ){ + sqlite3_int64 iVal = sqlite3_value_int64(argv[ii]); +#ifdef SQLITE_RTREE_INT_ONLY + p->u.rValue = iVal; +#else + p->u.rValue = (double)iVal; + if( iVal>=((sqlite3_int64)1)<<48 + || iVal<=-(((sqlite3_int64)1)<<48) + ){ + if( p->op==RTREE_LT ) p->op = RTREE_LE; + if( p->op==RTREE_GT ) p->op = RTREE_GE; + } +#endif + }else if( eType==SQLITE_FLOAT ){ #ifdef SQLITE_RTREE_INT_ONLY p->u.rValue = sqlite3_value_int64(argv[ii]); #else @@ -205144,11 +207145,12 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) ){ u8 op; + u8 doOmit = 1; switch( p->op ){ - case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; - case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; + case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; doOmit = 0; break; + case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; doOmit = 0; break; case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break; - case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break; + case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; doOmit = 0; break; case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break; case SQLITE_INDEX_CONSTRAINT_MATCH: op = RTREE_MATCH; break; default: op = 0; break; @@ -205157,7 +207159,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ zIdxStr[iIdx++] = op; zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0'); pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); - pIdxInfo->aConstraintUsage[ii].omit = 1; + pIdxInfo->aConstraintUsage[ii].omit = doOmit; } } } @@ -218645,6 +220647,7 @@ static int sessionPreupdateEqual( rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal); } assert( rc==SQLITE_OK ); + (void)rc; /* Suppress warning about unused variable */ if( sqlite3_value_type(pVal)!=eType ) return 0; /* A SessionChange object never has a NULL value in a PK column */ @@ -220989,15 +222992,19 @@ static int sessionReadRecord( } } if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ - sqlite3_int64 v = sessionGetI64(aVal); - if( eType==SQLITE_INTEGER ){ - sqlite3VdbeMemSetInt64(apOut[i], v); + if( (pIn->nData-pIn->iNext)<8 ){ + rc = SQLITE_CORRUPT_BKPT; }else{ - double d; - memcpy(&d, &v, 8); - sqlite3VdbeMemSetDouble(apOut[i], d); + sqlite3_int64 v = sessionGetI64(aVal); + if( eType==SQLITE_INTEGER ){ + sqlite3VdbeMemSetInt64(apOut[i], v); + }else{ + double d; + memcpy(&d, &v, 8); + sqlite3VdbeMemSetDouble(apOut[i], d); + } + pIn->iNext += 8; } - pIn->iNext += 8; } } } @@ -224060,7 +226067,7 @@ struct Fts5PhraseIter { ** See xPhraseFirstColumn above. */ struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 3 */ + int iVersion; /* Currently always set to 2 */ void *(*xUserData)(Fts5Context*); @@ -224289,8 +226296,8 @@ struct Fts5ExtensionApi { ** as separate queries of the FTS index are required for each synonym. ** ** When using methods (2) or (3), it is important that the tokenizer only -** provide synonyms when tokenizing document text (method (2)) or query -** text (method (3)), not both. Doing so will not cause any errors, but is +** provide synonyms when tokenizing document text (method (3)) or query +** text (method (2)), not both. Doing so will not cause any errors, but is ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; @@ -224338,7 +226345,7 @@ struct fts5_api { int (*xCreateTokenizer)( fts5_api *pApi, const char *zName, - void *pContext, + void *pUserData, fts5_tokenizer *pTokenizer, void (*xDestroy)(void*) ); @@ -224347,7 +226354,7 @@ struct fts5_api { int (*xFindTokenizer)( fts5_api *pApi, const char *zName, - void **ppContext, + void **ppUserData, fts5_tokenizer *pTokenizer ); @@ -224355,7 +226362,7 @@ struct fts5_api { int (*xCreateFunction)( fts5_api *pApi, const char *zName, - void *pContext, + void *pUserData, fts5_extension_function xFunction, void (*xDestroy)(void*) ); @@ -224527,6 +226534,10 @@ typedef struct Fts5Config Fts5Config; ** attempt to merge together. A value of 1 sets the object to use the ** compile time default. Zero disables auto-merge altogether. ** +** bContentlessDelete: +** True if the contentless_delete option was present in the CREATE +** VIRTUAL TABLE statement. +** ** zContent: ** ** zContentRowid: @@ -224561,6 +226572,7 @@ struct Fts5Config { int nPrefix; /* Number of prefix indexes */ int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ int eContent; /* An FTS5_CONTENT value */ + int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ char *zContent; /* content table */ char *zContentRowid; /* "content_rowid=" option value */ int bColumnsize; /* "columnsize=" option value (dflt==1) */ @@ -224582,6 +226594,7 @@ struct Fts5Config { char *zRank; /* Name of rank function */ char *zRankArgs; /* Arguments to rank function */ int bSecureDelete; /* 'secure-delete' */ + int nDeleteMerge; /* 'deletemerge' */ /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ char **pzErrmsg; @@ -224904,6 +226917,9 @@ static int sqlite3Fts5IndexReset(Fts5Index *p); static int sqlite3Fts5IndexLoadConfig(Fts5Index *p); +static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin); +static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid); + /* ** End of interface to code in fts5_index.c. **************************************************************************/ @@ -224988,6 +227004,11 @@ static int sqlite3Fts5HashWrite( */ static void sqlite3Fts5HashClear(Fts5Hash*); +/* +** Return true if the hash is empty, false otherwise. +*/ +static int sqlite3Fts5HashIsEmpty(Fts5Hash*); + static int sqlite3Fts5HashQuery( Fts5Hash*, /* Hash table to query */ int nPre, @@ -225009,6 +227030,7 @@ static void sqlite3Fts5HashScanEntry(Fts5Hash *, ); + /* ** End of interface to code in fts5_hash.c. **************************************************************************/ @@ -225252,7 +227274,8 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); #define FTS5_STAR 15 /* This file is automatically generated by Lemon from input grammar -** source file "fts5parse.y". */ +** source file "fts5parse.y". +*/ /* ** 2000-05-29 ** @@ -227880,6 +229903,8 @@ static void sqlite3Fts5TermsetFree(Fts5Termset *p){ #define FTS5_DEFAULT_CRISISMERGE 16 #define FTS5_DEFAULT_HASHSIZE (1024*1024) +#define FTS5_DEFAULT_DELETE_AUTOMERGE 10 /* default 10% */ + /* Maximum allowed page size */ #define FTS5_MAX_PAGE_SIZE (64*1024) @@ -228210,6 +230235,16 @@ static int fts5ConfigParseSpecial( return rc; } + if( sqlite3_strnicmp("contentless_delete", zCmd, nCmd)==0 ){ + if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ + *pzErr = sqlite3_mprintf("malformed contentless_delete=... directive"); + rc = SQLITE_ERROR; + }else{ + pConfig->bContentlessDelete = (zArg[0]=='1'); + } + return rc; + } + if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){ if( pConfig->zContentRowid ){ *pzErr = sqlite3_mprintf("multiple content_rowid=... directives"); @@ -228454,6 +230489,28 @@ static int sqlite3Fts5ConfigParse( sqlite3_free(zTwo); } + /* We only allow contentless_delete=1 if the table is indeed contentless. */ + if( rc==SQLITE_OK + && pRet->bContentlessDelete + && pRet->eContent!=FTS5_CONTENT_NONE + ){ + *pzErr = sqlite3_mprintf( + "contentless_delete=1 requires a contentless table" + ); + rc = SQLITE_ERROR; + } + + /* We only allow contentless_delete=1 if columnsize=0 is not present. + ** + ** This restriction may be removed at some point. + */ + if( rc==SQLITE_OK && pRet->bContentlessDelete && pRet->bColumnsize==0 ){ + *pzErr = sqlite3_mprintf( + "contentless_delete=1 is incompatible with columnsize=0" + ); + rc = SQLITE_ERROR; + } + /* If a tokenizer= option was successfully parsed, the tokenizer has ** already been allocated. Otherwise, allocate an instance of the default ** tokenizer (unicode61) now. */ @@ -228748,6 +230805,18 @@ static int sqlite3Fts5ConfigSetValue( } } + else if( 0==sqlite3_stricmp(zKey, "deletemerge") ){ + int nVal = -1; + if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ + nVal = sqlite3_value_int(pVal); + }else{ + *pbBadkey = 1; + } + if( nVal<0 ) nVal = FTS5_DEFAULT_DELETE_AUTOMERGE; + if( nVal>100 ) nVal = 0; + pConfig->nDeleteMerge = nVal; + } + else if( 0==sqlite3_stricmp(zKey, "rank") ){ const char *zIn = (const char*)sqlite3_value_text(pVal); char *zRank; @@ -228796,6 +230865,7 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ pConfig->nUsermerge = FTS5_DEFAULT_USERMERGE; pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE; pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE; + pConfig->nDeleteMerge = FTS5_DEFAULT_DELETE_AUTOMERGE; zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName); if( zSql ){ @@ -231319,7 +233389,7 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd( return pRet; } -#ifdef SQLITE_TEST +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ sqlite3_int64 nByte = 0; Fts5ExprTerm *p; @@ -231425,6 +233495,8 @@ static char *fts5ExprPrintTcl( if( zRet==0 ) return 0; } + }else if( pExpr->eType==0 ){ + zRet = sqlite3_mprintf("{}"); }else{ char const *zOp = 0; int i; @@ -231686,14 +233758,14 @@ static void fts5ExprFold( sqlite3_result_int(pCtx, sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics)); } } -#endif /* ifdef SQLITE_TEST */ +#endif /* if SQLITE_TEST || SQLITE_FTS5_DEBUG */ /* ** This is called during initialization to register the fts5_expr() scalar ** UDF with the SQLite handle passed as the only argument. */ static int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){ -#ifdef SQLITE_TEST +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) struct Fts5ExprFunc { const char *z; void (*x)(sqlite3_context*,int,sqlite3_value**); @@ -232453,7 +234525,6 @@ static int fts5HashEntrySort( pList = fts5HashEntryMerge(pList, ap[i]); } - pHash->nEntry = 0; sqlite3_free(ap); *ppSorted = pList; return SQLITE_OK; @@ -232507,6 +234578,28 @@ static int sqlite3Fts5HashScanInit( return fts5HashEntrySort(p, pTerm, nTerm, &p->pScan); } +#ifdef SQLITE_DEBUG +static int fts5HashCount(Fts5Hash *pHash){ + int nEntry = 0; + int ii; + for(ii=0; iinSlot; ii++){ + Fts5HashEntry *p = 0; + for(p=pHash->aSlot[ii]; p; p=p->pHashNext){ + nEntry++; + } + } + return nEntry; +} +#endif + +/* +** Return true if the hash table is empty, false otherwise. +*/ +static int sqlite3Fts5HashIsEmpty(Fts5Hash *pHash){ + assert( pHash->nEntry==fts5HashCount(pHash) ); + return pHash->nEntry==0; +} + static void sqlite3Fts5HashScanNext(Fts5Hash *p){ assert( !sqlite3Fts5HashScanEof(p) ); p->pScan = p->pScan->pScanNext; @@ -232595,6 +234688,24 @@ static void sqlite3Fts5HashScanEntry( #define FTS5_MAX_LEVEL 64 +/* +** There are two versions of the format used for the structure record: +** +** 1. the legacy format, that may be read by all fts5 versions, and +** +** 2. the V2 format, which is used by contentless_delete=1 databases. +** +** Both begin with a 4-byte "configuration cookie" value. Then, a legacy +** format structure record contains a varint - the number of levels in +** the structure. Whereas a V2 structure record contains the constant +** 4 bytes [0xff 0x00 0x00 0x01]. This is unambiguous as the value of a +** varint has to be at least 16256 to begin with "0xFF". And the default +** maximum number of levels is 64. +** +** See below for more on structure record formats. +*/ +#define FTS5_STRUCTURE_V2 "\xFF\x00\x00\x01" + /* ** Details: ** @@ -232602,7 +234713,7 @@ static void sqlite3Fts5HashScanEntry( ** ** CREATE TABLE %_data(id INTEGER PRIMARY KEY, block BLOB); ** -** , contains the following 5 types of records. See the comments surrounding +** , contains the following 6 types of records. See the comments surrounding ** the FTS5_*_ROWID macros below for a description of how %_data rowids are ** assigned to each fo them. ** @@ -232611,12 +234722,12 @@ static void sqlite3Fts5HashScanEntry( ** The set of segments that make up an index - the index structure - are ** recorded in a single record within the %_data table. The record consists ** of a single 32-bit configuration cookie value followed by a list of -** SQLite varints. If the FTS table features more than one index (because -** there are one or more prefix indexes), it is guaranteed that all share -** the same cookie value. +** SQLite varints. ** -** Immediately following the configuration cookie, the record begins with -** three varints: +** If the structure record is a V2 record, the configuration cookie is +** followed by the following 4 bytes: [0xFF 0x00 0x00 0x01]. +** +** Next, the record continues with three varints: ** ** + number of levels, ** + total number of segments on all levels, @@ -232631,6 +234742,12 @@ static void sqlite3Fts5HashScanEntry( ** + first leaf page number (often 1, always greater than 0) ** + final leaf page number ** +** Then, for V2 structures only: +** +** + lower origin counter value, +** + upper origin counter value, +** + the number of tombstone hash pages. +** ** 2. The Averages Record: ** ** A single record within the %_data table. The data is a list of varints. @@ -232746,6 +234863,38 @@ static void sqlite3Fts5HashScanEntry( ** * A list of delta-encoded varints - the first rowid on each subsequent ** child page. ** +** 6. Tombstone Hash Page +** +** These records are only ever present in contentless_delete=1 tables. +** There are zero or more of these associated with each segment. They +** are used to store the tombstone rowids for rows contained in the +** associated segments. +** +** The set of nHashPg tombstone hash pages associated with a single +** segment together form a single hash table containing tombstone rowids. +** To find the page of the hash on which a key might be stored: +** +** iPg = (rowid % nHashPg) +** +** Then, within page iPg, which has nSlot slots: +** +** iSlot = (rowid / nHashPg) % nSlot +** +** Each tombstone hash page begins with an 8 byte header: +** +** 1-byte: Key-size (the size in bytes of each slot). Either 4 or 8. +** 1-byte: rowid-0-tombstone flag. This flag is only valid on the +** first tombstone hash page for each segment (iPg=0). If set, +** the hash table contains rowid 0. If clear, it does not. +** Rowid 0 is handled specially. +** 2-bytes: unused. +** 4-bytes: Big-endian integer containing number of entries on page. +** +** Following this are nSlot 4 or 8 byte slots (depending on the key-size +** in the first byte of the page header). The number of slots may be +** determined based on the size of the page record and the key-size: +** +** nSlot = (nByte - 8) / key-size */ /* @@ -232779,6 +234928,7 @@ static void sqlite3Fts5HashScanEntry( #define FTS5_SEGMENT_ROWID(segid, pgno) fts5_dri(segid, 0, 0, pgno) #define FTS5_DLIDX_ROWID(segid, height, pgno) fts5_dri(segid, 1, height, pgno) +#define FTS5_TOMBSTONE_ROWID(segid,ipg) fts5_dri(segid+(1<<16), 0, 0, ipg) #ifdef SQLITE_DEBUG static int sqlite3Fts5Corrupt() { return SQLITE_CORRUPT_VTAB; } @@ -232814,6 +234964,12 @@ struct Fts5Data { /* ** One object per %_data table. +** +** nContentlessDelete: +** The number of contentless delete operations since the most recent +** call to fts5IndexFlush() or fts5IndexDiscardData(). This is tracked +** so that extra auto-merge work can be done by fts5IndexFlush() to +** account for the delete operations. */ struct Fts5Index { Fts5Config *pConfig; /* Virtual table configuration */ @@ -232828,6 +234984,8 @@ struct Fts5Index { int nPendingData; /* Current bytes of pending data */ i64 iWriteRowid; /* Rowid for current doc being written */ int bDelete; /* Current write is a delete */ + int nContentlessDelete; /* Number of contentless delete ops */ + int nPendingRow; /* Number of INSERT in hash table */ /* Error state. */ int rc; /* Current error code */ @@ -232862,11 +235020,23 @@ struct Fts5DoclistIter { ** The contents of the "structure" record for each index are represented ** using an Fts5Structure record in memory. Which uses instances of the ** other Fts5StructureXXX types as components. +** +** nOriginCntr: +** This value is set to non-zero for structure records created for +** contentlessdelete=1 tables only. In that case it represents the +** origin value to apply to the next top-level segment created. */ struct Fts5StructureSegment { int iSegid; /* Segment id */ int pgnoFirst; /* First leaf page number in segment */ int pgnoLast; /* Last leaf page number in segment */ + + /* contentlessdelete=1 tables only: */ + u64 iOrigin1; + u64 iOrigin2; + int nPgTombstone; /* Number of tombstone hash table pages */ + u64 nEntryTombstone; /* Number of tombstone entries that "count" */ + u64 nEntry; /* Number of rows in this segment */ }; struct Fts5StructureLevel { int nMerge; /* Number of segments in incr-merge */ @@ -232876,6 +235046,7 @@ struct Fts5StructureLevel { struct Fts5Structure { int nRef; /* Object reference count */ u64 nWriteCounter; /* Total leaves written to level 0 */ + u64 nOriginCntr; /* Origin value for next top-level segment */ int nSegment; /* Total segments in this structure */ int nLevel; /* Number of levels in this index */ Fts5StructureLevel aLevel[1]; /* Array of nLevel level objects */ @@ -232964,6 +235135,13 @@ struct Fts5CResult { ** ** iTermIdx: ** Index of current term on iTermLeafPgno. +** +** apTombstone/nTombstone: +** These are used for contentless_delete=1 tables only. When the cursor +** is first allocated, the apTombstone[] array is allocated so that it +** is large enough for all tombstones hash pages associated with the +** segment. The pages themselves are loaded lazily from the database as +** they are required. */ struct Fts5SegIter { Fts5StructureSegment *pSeg; /* Segment to iterate through */ @@ -232972,6 +235150,8 @@ struct Fts5SegIter { Fts5Data *pLeaf; /* Current leaf data */ Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */ i64 iLeafOffset; /* Byte offset within current leaf */ + Fts5Data **apTombstone; /* Array of tombstone pages */ + int nTombstone; /* Next method */ void (*xNext)(Fts5Index*, Fts5SegIter*, int*); @@ -233101,6 +235281,60 @@ static u16 fts5GetU16(const u8 *aIn){ return ((u16)aIn[0] << 8) + aIn[1]; } +/* +** The only argument points to a buffer at least 8 bytes in size. This +** function interprets the first 8 bytes of the buffer as a 64-bit big-endian +** unsigned integer and returns the result. +*/ +static u64 fts5GetU64(u8 *a){ + return ((u64)a[0] << 56) + + ((u64)a[1] << 48) + + ((u64)a[2] << 40) + + ((u64)a[3] << 32) + + ((u64)a[4] << 24) + + ((u64)a[5] << 16) + + ((u64)a[6] << 8) + + ((u64)a[7] << 0); +} + +/* +** The only argument points to a buffer at least 4 bytes in size. This +** function interprets the first 4 bytes of the buffer as a 32-bit big-endian +** unsigned integer and returns the result. +*/ +static u32 fts5GetU32(const u8 *a){ + return ((u32)a[0] << 24) + + ((u32)a[1] << 16) + + ((u32)a[2] << 8) + + ((u32)a[3] << 0); +} + +/* +** Write iVal, formated as a 64-bit big-endian unsigned integer, to the +** buffer indicated by the first argument. +*/ +static void fts5PutU64(u8 *a, u64 iVal){ + a[0] = ((iVal >> 56) & 0xFF); + a[1] = ((iVal >> 48) & 0xFF); + a[2] = ((iVal >> 40) & 0xFF); + a[3] = ((iVal >> 32) & 0xFF); + a[4] = ((iVal >> 24) & 0xFF); + a[5] = ((iVal >> 16) & 0xFF); + a[6] = ((iVal >> 8) & 0xFF); + a[7] = ((iVal >> 0) & 0xFF); +} + +/* +** Write iVal, formated as a 32-bit big-endian unsigned integer, to the +** buffer indicated by the first argument. +*/ +static void fts5PutU32(u8 *a, u32 iVal){ + a[0] = ((iVal >> 24) & 0xFF); + a[1] = ((iVal >> 16) & 0xFF); + a[2] = ((iVal >> 8) & 0xFF); + a[3] = ((iVal >> 0) & 0xFF); +} + /* ** Allocate and return a buffer at least nByte bytes in size. ** @@ -233328,10 +235562,17 @@ static void fts5DataDelete(Fts5Index *p, i64 iFirst, i64 iLast){ /* ** Remove all records associated with segment iSegid. */ -static void fts5DataRemoveSegment(Fts5Index *p, int iSegid){ +static void fts5DataRemoveSegment(Fts5Index *p, Fts5StructureSegment *pSeg){ + int iSegid = pSeg->iSegid; i64 iFirst = FTS5_SEGMENT_ROWID(iSegid, 0); i64 iLast = FTS5_SEGMENT_ROWID(iSegid+1, 0)-1; fts5DataDelete(p, iFirst, iLast); + + if( pSeg->nPgTombstone ){ + i64 iTomb1 = FTS5_TOMBSTONE_ROWID(iSegid, 0); + i64 iTomb2 = FTS5_TOMBSTONE_ROWID(iSegid, pSeg->nPgTombstone-1); + fts5DataDelete(p, iTomb1, iTomb2); + } if( p->pIdxDeleter==0 ){ Fts5Config *pConfig = p->pConfig; fts5IndexPrepareStmt(p, &p->pIdxDeleter, sqlite3_mprintf( @@ -233442,11 +235683,19 @@ static int fts5StructureDecode( int nSegment = 0; sqlite3_int64 nByte; /* Bytes of space to allocate at pRet */ Fts5Structure *pRet = 0; /* Structure object to return */ + int bStructureV2 = 0; /* True for FTS5_STRUCTURE_V2 */ + u64 nOriginCntr = 0; /* Largest origin value seen so far */ /* Grab the cookie value */ if( piCookie ) *piCookie = sqlite3Fts5Get32(pData); i = 4; + /* Check if this is a V2 structure record. Set bStructureV2 if it is. */ + if( 0==memcmp(&pData[i], FTS5_STRUCTURE_V2, 4) ){ + i += 4; + bStructureV2 = 1; + } + /* Read the total number of levels and segments from the start of the ** structure record. */ i += fts5GetVarint32(&pData[i], nLevel); @@ -233497,6 +235746,14 @@ static int fts5StructureDecode( i += fts5GetVarint32(&pData[i], pSeg->iSegid); i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst); i += fts5GetVarint32(&pData[i], pSeg->pgnoLast); + if( bStructureV2 ){ + i += fts5GetVarint(&pData[i], &pSeg->iOrigin1); + i += fts5GetVarint(&pData[i], &pSeg->iOrigin2); + i += fts5GetVarint32(&pData[i], pSeg->nPgTombstone); + i += fts5GetVarint(&pData[i], &pSeg->nEntryTombstone); + i += fts5GetVarint(&pData[i], &pSeg->nEntry); + nOriginCntr = MAX(nOriginCntr, pSeg->iOrigin2); + } if( pSeg->pgnoLastpgnoFirst ){ rc = FTS5_CORRUPT; break; @@ -233507,6 +235764,9 @@ static int fts5StructureDecode( } } if( nSegment!=0 && rc==SQLITE_OK ) rc = FTS5_CORRUPT; + if( bStructureV2 ){ + pRet->nOriginCntr = nOriginCntr+1; + } if( rc!=SQLITE_OK ){ fts5StructureRelease(pRet); @@ -233719,6 +235979,7 @@ static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){ Fts5Buffer buf; /* Buffer to serialize record into */ int iLvl; /* Used to iterate through levels */ int iCookie; /* Cookie value to store */ + int nHdr = (pStruct->nOriginCntr>0 ? (4+4+9+9+9) : (4+9+9)); assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) ); memset(&buf, 0, sizeof(Fts5Buffer)); @@ -233727,9 +235988,12 @@ static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){ iCookie = p->pConfig->iCookie; if( iCookie<0 ) iCookie = 0; - if( 0==sqlite3Fts5BufferSize(&p->rc, &buf, 4+9+9+9) ){ + if( 0==sqlite3Fts5BufferSize(&p->rc, &buf, nHdr) ){ sqlite3Fts5Put32(buf.p, iCookie); buf.n = 4; + if( pStruct->nOriginCntr>0 ){ + fts5BufferSafeAppendBlob(&buf, FTS5_STRUCTURE_V2, 4); + } fts5BufferSafeAppendVarint(&buf, pStruct->nLevel); fts5BufferSafeAppendVarint(&buf, pStruct->nSegment); fts5BufferSafeAppendVarint(&buf, (i64)pStruct->nWriteCounter); @@ -233743,9 +236007,17 @@ static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){ assert( pLvl->nMerge<=pLvl->nSeg ); for(iSeg=0; iSegnSeg; iSeg++){ - fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].iSegid); - fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoFirst); - fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoLast); + Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; + fts5BufferAppendVarint(&p->rc, &buf, pSeg->iSegid); + fts5BufferAppendVarint(&p->rc, &buf, pSeg->pgnoFirst); + fts5BufferAppendVarint(&p->rc, &buf, pSeg->pgnoLast); + if( pStruct->nOriginCntr>0 ){ + fts5BufferAppendVarint(&p->rc, &buf, pSeg->iOrigin1); + fts5BufferAppendVarint(&p->rc, &buf, pSeg->iOrigin2); + fts5BufferAppendVarint(&p->rc, &buf, pSeg->nPgTombstone); + fts5BufferAppendVarint(&p->rc, &buf, pSeg->nEntryTombstone); + fts5BufferAppendVarint(&p->rc, &buf, pSeg->nEntry); + } } } @@ -234268,6 +236540,23 @@ static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){ } } +/* +** Allocate a tombstone hash page array (pIter->apTombstone) for the +** iterator passed as the second argument. If an OOM error occurs, leave +** an error in the Fts5Index object. +*/ +static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ + const int nTomb = pIter->pSeg->nPgTombstone; + if( nTomb>0 ){ + Fts5Data **apTomb = 0; + apTomb = (Fts5Data**)sqlite3Fts5MallocZero(&p->rc, sizeof(Fts5Data)*nTomb); + if( apTomb ){ + pIter->apTombstone = apTomb; + pIter->nTombstone = nTomb; + } + } +} + /* ** Initialize the iterator object pIter to iterate through the entries in ** segment pSeg. The iterator is left pointing to the first entry when @@ -234309,6 +236598,7 @@ static void fts5SegIterInit( pIter->iPgidxOff = pIter->pLeaf->szLeaf+1; fts5SegIterLoadTerm(p, pIter, 0); fts5SegIterLoadNPos(p, pIter); + fts5SegIterAllocTombstone(p, pIter); } } @@ -235010,6 +237300,7 @@ static void fts5SegIterSeekInit( } fts5SegIterSetNext(p, pIter); + fts5SegIterAllocTombstone(p, pIter); /* Either: ** @@ -235090,6 +237381,20 @@ static void fts5SegIterHashInit( fts5SegIterSetNext(p, pIter); } +/* +** Array ap[] contains n elements. Release each of these elements using +** fts5DataRelease(). Then free the array itself using sqlite3_free(). +*/ +static void fts5IndexFreeArray(Fts5Data **ap, int n){ + if( ap ){ + int ii; + for(ii=0; iiterm); fts5DataRelease(pIter->pLeaf); fts5DataRelease(pIter->pNextLeaf); + fts5IndexFreeArray(pIter->apTombstone, pIter->nTombstone); fts5DlidxIterFree(pIter->pDlidx); sqlite3_free(pIter->aRowidOffset); memset(pIter, 0, sizeof(Fts5SegIter)); @@ -235434,6 +237740,84 @@ static void fts5MultiIterSetEof(Fts5Iter *pIter){ pIter->iSwitchRowid = pSeg->iRowid; } +/* +** The argument to this macro must be an Fts5Data structure containing a +** tombstone hash page. This macro returns the key-size of the hash-page. +*/ +#define TOMBSTONE_KEYSIZE(pPg) (pPg->p[0]==4 ? 4 : 8) + +#define TOMBSTONE_NSLOT(pPg) \ + ((pPg->nn > 16) ? ((pPg->nn-8) / TOMBSTONE_KEYSIZE(pPg)) : 1) + +/* +** Query a single tombstone hash table for rowid iRowid. Return true if +** it is found or false otherwise. The tombstone hash table is one of +** nHashTable tables. +*/ +static int fts5IndexTombstoneQuery( + Fts5Data *pHash, /* Hash table page to query */ + int nHashTable, /* Number of pages attached to segment */ + u64 iRowid /* Rowid to query hash for */ +){ + const int szKey = TOMBSTONE_KEYSIZE(pHash); + const int nSlot = TOMBSTONE_NSLOT(pHash); + int iSlot = (iRowid / nHashTable) % nSlot; + int nCollide = nSlot; + + if( iRowid==0 ){ + return pHash->p[1]; + }else if( szKey==4 ){ + u32 *aSlot = (u32*)&pHash->p[8]; + while( aSlot[iSlot] ){ + if( fts5GetU32((u8*)&aSlot[iSlot])==iRowid ) return 1; + if( nCollide--==0 ) break; + iSlot = (iSlot+1)%nSlot; + } + }else{ + u64 *aSlot = (u64*)&pHash->p[8]; + while( aSlot[iSlot] ){ + if( fts5GetU64((u8*)&aSlot[iSlot])==iRowid ) return 1; + if( nCollide--==0 ) break; + iSlot = (iSlot+1)%nSlot; + } + } + + return 0; +} + +/* +** Return true if the iterator passed as the only argument points +** to an segment entry for which there is a tombstone. Return false +** if there is no tombstone or if the iterator is already at EOF. +*/ +static int fts5MultiIterIsDeleted(Fts5Iter *pIter){ + int iFirst = pIter->aFirst[1].iFirst; + Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; + + if( pSeg->pLeaf && pSeg->nTombstone ){ + /* Figure out which page the rowid might be present on. */ + int iPg = ((u64)pSeg->iRowid) % pSeg->nTombstone; + assert( iPg>=0 ); + + /* If tombstone hash page iPg has not yet been loaded from the + ** database, load it now. */ + if( pSeg->apTombstone[iPg]==0 ){ + pSeg->apTombstone[iPg] = fts5DataRead(pIter->pIndex, + FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg) + ); + if( pSeg->apTombstone[iPg]==0 ) return 0; + } + + return fts5IndexTombstoneQuery( + pSeg->apTombstone[iPg], + pSeg->nTombstone, + pSeg->iRowid + ); + } + + return 0; +} + /* ** Move the iterator to the next entry. ** @@ -235471,7 +237855,9 @@ static void fts5MultiIterNext( fts5AssertMultiIterSetup(p, pIter); assert( pSeg==&pIter->aSeg[pIter->aFirst[1].iFirst] && pSeg->pLeaf ); - if( pIter->bSkipEmpty==0 || pSeg->nPos ){ + if( (pIter->bSkipEmpty==0 || pSeg->nPos) + && 0==fts5MultiIterIsDeleted(pIter) + ){ pIter->xSetOutputs(pIter, pSeg); return; } @@ -235503,7 +237889,9 @@ static void fts5MultiIterNext2( } fts5AssertMultiIterSetup(p, pIter); - }while( fts5MultiIterIsEmpty(p, pIter) ); + }while( (fts5MultiIterIsEmpty(p, pIter) || fts5MultiIterIsDeleted(pIter)) + && (p->rc==SQLITE_OK) + ); } } @@ -236058,7 +238446,9 @@ static void fts5MultiIterNew( fts5MultiIterSetEof(pNew); fts5AssertMultiIterSetup(p, pNew); - if( pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew) ){ + if( (pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew)) + || fts5MultiIterIsDeleted(pNew) + ){ fts5MultiIterNext(p, pNew, 0, 0); }else if( pNew->base.bEof==0 ){ Fts5SegIter *pSeg = &pNew->aSeg[pNew->aFirst[1].iFirst]; @@ -236236,7 +238626,9 @@ static void fts5IndexDiscardData(Fts5Index *p){ if( p->pHash ){ sqlite3Fts5HashClear(p->pHash); p->nPendingData = 0; + p->nPendingRow = 0; } + p->nContentlessDelete = 0; } /* @@ -236873,6 +239265,12 @@ static void fts5IndexMergeLevel( /* Read input from all segments in the input level */ nInput = pLvl->nSeg; + + /* Set the range of origins that will go into the output segment. */ + if( pStruct->nOriginCntr>0 ){ + pSeg->iOrigin1 = pLvl->aSeg[0].iOrigin1; + pSeg->iOrigin2 = pLvl->aSeg[pLvl->nSeg-1].iOrigin2; + } } bOldest = (pLvlOut->nSeg==1 && pStruct->nLevel==iLvl+2); @@ -236932,8 +239330,11 @@ static void fts5IndexMergeLevel( int i; /* Remove the redundant segments from the %_data table */ + assert( pSeg->nEntry==0 ); for(i=0; iaSeg[i].iSegid); + Fts5StructureSegment *pOld = &pLvl->aSeg[i]; + pSeg->nEntry += (pOld->nEntry - pOld->nEntryTombstone); + fts5DataRemoveSegment(p, pOld); } /* Remove the redundant segments from the input level */ @@ -236959,6 +239360,43 @@ static void fts5IndexMergeLevel( if( pnRem ) *pnRem -= writer.nLeafWritten; } +/* +** If this is not a contentless_delete=1 table, or if the 'deletemerge' +** configuration option is set to 0, then this function always returns -1. +** Otherwise, it searches the structure object passed as the second argument +** for a level suitable for merging due to having a large number of +** tombstones in the tombstone hash. If one is found, its index is returned. +** Otherwise, if there is no suitable level, -1. +*/ +static int fts5IndexFindDeleteMerge(Fts5Index *p, Fts5Structure *pStruct){ + Fts5Config *pConfig = p->pConfig; + int iRet = -1; + if( pConfig->bContentlessDelete && pConfig->nDeleteMerge>0 ){ + int ii; + int nBest = 0; + + for(ii=0; iinLevel; ii++){ + Fts5StructureLevel *pLvl = &pStruct->aLevel[ii]; + i64 nEntry = 0; + i64 nTomb = 0; + int iSeg; + for(iSeg=0; iSegnSeg; iSeg++){ + nEntry += pLvl->aSeg[iSeg].nEntry; + nTomb += pLvl->aSeg[iSeg].nEntryTombstone; + } + assert_nc( nEntry>0 || pLvl->nSeg==0 ); + if( nEntry>0 ){ + int nPercent = (nTomb * 100) / nEntry; + if( nPercent>=pConfig->nDeleteMerge && nPercent>nBest ){ + iRet = ii; + nBest = nPercent; + } + } + } + } + return iRet; +} + /* ** Do up to nPg pages of automerge work on the index. ** @@ -236978,14 +239416,15 @@ static int fts5IndexMerge( int iBestLvl = 0; /* Level offering the most input segments */ int nBest = 0; /* Number of input segments on best level */ - /* Set iBestLvl to the level to read input segments from. */ + /* Set iBestLvl to the level to read input segments from. Or to -1 if + ** there is no level suitable to merge segments from. */ assert( pStruct->nLevel>0 ); for(iLvl=0; iLvlnLevel; iLvl++){ Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; if( pLvl->nMerge ){ if( pLvl->nMerge>nBest ){ iBestLvl = iLvl; - nBest = pLvl->nMerge; + nBest = nMin; } break; } @@ -236994,22 +239433,18 @@ static int fts5IndexMerge( iBestLvl = iLvl; } } - - /* If nBest is still 0, then the index must be empty. */ -#ifdef SQLITE_DEBUG - for(iLvl=0; nBest==0 && iLvlnLevel; iLvl++){ - assert( pStruct->aLevel[iLvl].nSeg==0 ); + if( nBestaLevel[iBestLvl].nMerge==0 ){ - break; - } + if( iBestLvl<0 ) break; bRet = 1; fts5IndexMergeLevel(p, &pStruct, iBestLvl, &nRem); if( p->rc==SQLITE_OK && pStruct->aLevel[iBestLvl].nMerge==0 ){ fts5StructurePromote(p, iBestLvl+1, pStruct); } + + if( nMin==1 ) nMin = 2; } *ppStruct = pStruct; return bRet; @@ -237175,7 +239610,7 @@ static void fts5SecureDeleteOverflow( pLeaf = 0; }else if( bDetailNone ){ break; - }else if( iNext>=pLeaf->szLeaf || iNext<4 ){ + }else if( iNext>=pLeaf->szLeaf || pLeaf->nnszLeaf || iNext<4 ){ p->rc = FTS5_CORRUPT; break; }else{ @@ -237194,9 +239629,13 @@ static void fts5SecureDeleteOverflow( int i1 = pLeaf->szLeaf; int i2 = 0; + i1 += fts5GetVarint32(&aPg[i1], iFirst); + if( iFirstrc = FTS5_CORRUPT; + break; + } aIdx = sqlite3Fts5MallocZero(&p->rc, (pLeaf->nn-pLeaf->szLeaf)+2); if( aIdx==0 ) break; - i1 += fts5GetVarint32(&aPg[i1], iFirst); i2 = sqlite3Fts5PutVarint(aIdx, iFirst-nShift); if( i1nn ){ memcpy(&aIdx[i2], &aPg[i1], pLeaf->nn-i1); @@ -237379,7 +239818,9 @@ static void fts5DoSecureDelete( iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix); } iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix); - if( nPrefix2>nPrefix ){ + if( nPrefix2>pSeg->term.n ){ + p->rc = FTS5_CORRUPT; + }else if( nPrefix2>nPrefix ){ memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix); iOff += (nPrefix2-nPrefix); } @@ -237389,80 +239830,79 @@ static void fts5DoSecureDelete( } } }else if( iStart==4 ){ - int iPgno; + int iPgno; - assert_nc( pSeg->iLeafPgno>pSeg->iTermLeafPgno ); - /* The entry being removed may be the only position list in - ** its doclist. */ - for(iPgno=pSeg->iLeafPgno-1; iPgno>pSeg->iTermLeafPgno; iPgno-- ){ - Fts5Data *pPg = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno)); - int bEmpty = (pPg && pPg->nn==4); - fts5DataRelease(pPg); - if( bEmpty==0 ) break; - } + assert_nc( pSeg->iLeafPgno>pSeg->iTermLeafPgno ); + /* The entry being removed may be the only position list in + ** its doclist. */ + for(iPgno=pSeg->iLeafPgno-1; iPgno>pSeg->iTermLeafPgno; iPgno-- ){ + Fts5Data *pPg = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno)); + int bEmpty = (pPg && pPg->nn==4); + fts5DataRelease(pPg); + if( bEmpty==0 ) break; + } - if( iPgno==pSeg->iTermLeafPgno ){ - i64 iId = FTS5_SEGMENT_ROWID(iSegid, pSeg->iTermLeafPgno); - Fts5Data *pTerm = fts5DataRead(p, iId); - if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){ - u8 *aTermIdx = &pTerm->p[pTerm->szLeaf]; - int nTermIdx = pTerm->nn - pTerm->szLeaf; - int iTermIdx = 0; - int iTermOff = 0; + if( iPgno==pSeg->iTermLeafPgno ){ + i64 iId = FTS5_SEGMENT_ROWID(iSegid, pSeg->iTermLeafPgno); + Fts5Data *pTerm = fts5DataRead(p, iId); + if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){ + u8 *aTermIdx = &pTerm->p[pTerm->szLeaf]; + int nTermIdx = pTerm->nn - pTerm->szLeaf; + int iTermIdx = 0; + int iTermOff = 0; - while( 1 ){ - u32 iVal = 0; - int nByte = fts5GetVarint32(&aTermIdx[iTermIdx], iVal); - iTermOff += iVal; - if( (iTermIdx+nByte)>=nTermIdx ) break; - iTermIdx += nByte; - } - nTermIdx = iTermIdx; - - memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); - fts5PutU16(&pTerm->p[2], iTermOff); - - fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); - if( nTermIdx==0 ){ - fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); - } + while( 1 ){ + u32 iVal = 0; + int nByte = fts5GetVarint32(&aTermIdx[iTermIdx], iVal); + iTermOff += iVal; + if( (iTermIdx+nByte)>=nTermIdx ) break; + iTermIdx += nByte; } - fts5DataRelease(pTerm); + nTermIdx = iTermIdx; + + memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); + fts5PutU16(&pTerm->p[2], iTermOff); + + fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); + if( nTermIdx==0 ){ + fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); + } + } + fts5DataRelease(pTerm); + } + } + + if( p->rc==SQLITE_OK ){ + const int nMove = nPg - iNextOff; /* Number of bytes to move */ + int nShift = iNextOff - iOff; /* Distance to move them */ + + int iPrevKeyOut = 0; + int iKeyIn = 0; + + memmove(&aPg[iOff], &aPg[iNextOff], nMove); + iPgIdx -= nShift; + nPg = iPgIdx; + fts5PutU16(&aPg[2], iPgIdx); + + for(iIdx=0; iIdxiOff ? nShift : 0)); + nPg += sqlite3Fts5PutVarint(&aPg[nPg], iKeyOut - iPrevKeyOut); + iPrevKeyOut = iKeyOut; } } - if( p->rc==SQLITE_OK ){ - const int nMove = nPg - iNextOff; - int nShift = 0; - - memmove(&aPg[iOff], &aPg[iNextOff], nMove); - iPgIdx -= (iNextOff - iOff); - nPg = iPgIdx; - fts5PutU16(&aPg[2], iPgIdx); - - nShift = iNextOff - iOff; - for(iIdx=0, iKeyOff=0, iPrevKeyOff=0; iIdxiOff ){ - iKeyOff -= nShift; - nShift = 0; - } - nPg += sqlite3Fts5PutVarint(&aPg[nPg], iKeyOff - iPrevKeyOff); - iPrevKeyOff = iKeyOff; - } - } - - if( iPgIdx==nPg && nIdx>0 && pSeg->iLeafPgno!=1 ){ - fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iLeafPgno); - } - - assert_nc( nPg>4 || fts5GetU16(aPg)==0 ); - fts5DataWrite(p, FTS5_SEGMENT_ROWID(iSegid,pSeg->iLeafPgno), aPg,nPg); + if( iPgIdx==nPg && nIdx>0 && pSeg->iLeafPgno!=1 ){ + fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iLeafPgno); } - sqlite3_free(aIdx); + + assert_nc( nPg>4 || fts5GetU16(aPg)==0 ); + fts5DataWrite(p, FTS5_SEGMENT_ROWID(iSegid,pSeg->iLeafPgno), aPg, nPg); + } + sqlite3_free(aIdx); } /* @@ -237516,187 +239956,197 @@ static void fts5FlushOneHash(Fts5Index *p){ /* Obtain a reference to the index structure and allocate a new segment-id ** for the new level-0 segment. */ pStruct = fts5StructureRead(p); - iSegid = fts5AllocateSegid(p, pStruct); fts5StructureInvalidate(p); - if( iSegid ){ - const int pgsz = p->pConfig->pgsz; - int eDetail = p->pConfig->eDetail; - int bSecureDelete = p->pConfig->bSecureDelete; - Fts5StructureSegment *pSeg; /* New segment within pStruct */ - Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ - Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ + if( sqlite3Fts5HashIsEmpty(pHash)==0 ){ + iSegid = fts5AllocateSegid(p, pStruct); + if( iSegid ){ + const int pgsz = p->pConfig->pgsz; + int eDetail = p->pConfig->eDetail; + int bSecureDelete = p->pConfig->bSecureDelete; + Fts5StructureSegment *pSeg; /* New segment within pStruct */ + Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ + Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ - Fts5SegWriter writer; - fts5WriteInit(p, &writer, iSegid); + Fts5SegWriter writer; + fts5WriteInit(p, &writer, iSegid); - pBuf = &writer.writer.buf; - pPgidx = &writer.writer.pgidx; + pBuf = &writer.writer.buf; + pPgidx = &writer.writer.pgidx; - /* fts5WriteInit() should have initialized the buffers to (most likely) - ** the maximum space required. */ - assert( p->rc || pBuf->nSpace>=(pgsz + FTS5_DATA_PADDING) ); - assert( p->rc || pPgidx->nSpace>=(pgsz + FTS5_DATA_PADDING) ); + /* fts5WriteInit() should have initialized the buffers to (most likely) + ** the maximum space required. */ + assert( p->rc || pBuf->nSpace>=(pgsz + FTS5_DATA_PADDING) ); + assert( p->rc || pPgidx->nSpace>=(pgsz + FTS5_DATA_PADDING) ); - /* Begin scanning through hash table entries. This loop runs once for each - ** term/doclist currently stored within the hash table. */ - if( p->rc==SQLITE_OK ){ - p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0); - } - while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){ - const char *zTerm; /* Buffer containing term */ - int nTerm; /* Size of zTerm in bytes */ - const u8 *pDoclist; /* Pointer to doclist for this term */ - int nDoclist; /* Size of doclist in bytes */ - - /* Get the term and doclist for this entry. */ - sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); - nTerm = (int)strlen(zTerm); - if( bSecureDelete==0 ){ - fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); - if( p->rc!=SQLITE_OK ) break; - assert( writer.bFirstRowidInPage==0 ); + /* Begin scanning through hash table entries. This loop runs once for each + ** term/doclist currently stored within the hash table. */ + if( p->rc==SQLITE_OK ){ + p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0); } + while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){ + const char *zTerm; /* Buffer containing term */ + int nTerm; /* Size of zTerm in bytes */ + const u8 *pDoclist; /* Pointer to doclist for this term */ + int nDoclist; /* Size of doclist in bytes */ - if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ - /* The entire doclist will fit on the current leaf. */ - fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist); - }else{ - int bTermWritten = !bSecureDelete; - i64 iRowid = 0; - i64 iPrev = 0; - int iOff = 0; + /* Get the term and doclist for this entry. */ + sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); + nTerm = (int)strlen(zTerm); + if( bSecureDelete==0 ){ + fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); + if( p->rc!=SQLITE_OK ) break; + assert( writer.bFirstRowidInPage==0 ); + } - /* The entire doclist will not fit on this leaf. The following - ** loop iterates through the poslists that make up the current - ** doclist. */ - while( p->rc==SQLITE_OK && iOff=(pBuf->n + pPgidx->n + nDoclist + 1) ){ + /* The entire doclist will fit on the current leaf. */ + fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist); + }else{ + int bTermWritten = !bSecureDelete; + i64 iRowid = 0; + i64 iPrev = 0; + int iOff = 0; - /* If in secure delete mode, and if this entry in the poslist is - ** in fact a delete, then edit the existing segments directly - ** using fts5FlushSecureDelete(). */ - if( bSecureDelete ){ - if( eDetail==FTS5_DETAIL_NONE ){ - if( iOffrc==SQLITE_OK && iOffrc!=SQLITE_OK || pDoclist[iOff]==0x01 ){ iOff++; - nDoclist = 0; - }else{ continue; } } - }else if( (pDoclist[iOff] & 0x01) ){ - fts5FlushSecureDelete(p, pStruct, zTerm, iRowid); - if( p->rc!=SQLITE_OK || pDoclist[iOff]==0x01 ){ - iOff++; - continue; - } } - } - if( p->rc==SQLITE_OK && bTermWritten==0 ){ - fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); - bTermWritten = 1; - assert( p->rc!=SQLITE_OK || writer.bFirstRowidInPage==0 ); - } + if( p->rc==SQLITE_OK && bTermWritten==0 ){ + fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); + bTermWritten = 1; + assert( p->rc!=SQLITE_OK || writer.bFirstRowidInPage==0 ); + } - if( writer.bFirstRowidInPage ){ - fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */ - pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); - writer.bFirstRowidInPage = 0; - fts5WriteDlidxAppend(p, &writer, iRowid); - }else{ - pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid-iPrev); - } - if( p->rc!=SQLITE_OK ) break; - assert( pBuf->n<=pBuf->nSpace ); - iPrev = iRowid; + if( writer.bFirstRowidInPage ){ + fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */ + pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); + writer.bFirstRowidInPage = 0; + fts5WriteDlidxAppend(p, &writer, iRowid); + }else{ + u64 iRowidDelta = (u64)iRowid - (u64)iPrev; + pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowidDelta); + } + if( p->rc!=SQLITE_OK ) break; + assert( pBuf->n<=pBuf->nSpace ); + iPrev = iRowid; - if( eDetail==FTS5_DETAIL_NONE ){ - if( iOffp[pBuf->n++] = 0; - iOff++; + if( eDetail==FTS5_DETAIL_NONE ){ if( iOffp[pBuf->n++] = 0; iOff++; + if( iOffp[pBuf->n++] = 0; + iOff++; + } + } + if( (pBuf->n + pPgidx->n)>=pgsz ){ + fts5WriteFlushLeaf(p, &writer); } - } - if( (pBuf->n + pPgidx->n)>=pgsz ){ - fts5WriteFlushLeaf(p, &writer); - } - }else{ - int bDummy; - int nPos; - int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy); - nCopy += nPos; - if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){ - /* The entire poslist will fit on the current leaf. So copy - ** it in one go. */ - fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy); }else{ - /* The entire poslist will not fit on this leaf. So it needs - ** to be broken into sections. The only qualification being - ** that each varint must be stored contiguously. */ - const u8 *pPoslist = &pDoclist[iOff]; - int iPos = 0; - while( p->rc==SQLITE_OK ){ - int nSpace = pgsz - pBuf->n - pPgidx->n; - int n = 0; - if( (nCopy - iPos)<=nSpace ){ - n = nCopy - iPos; - }else{ - n = fts5PoslistPrefix(&pPoslist[iPos], nSpace); + int bDummy; + int nPos; + int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy); + nCopy += nPos; + if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){ + /* The entire poslist will fit on the current leaf. So copy + ** it in one go. */ + fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy); + }else{ + /* The entire poslist will not fit on this leaf. So it needs + ** to be broken into sections. The only qualification being + ** that each varint must be stored contiguously. */ + const u8 *pPoslist = &pDoclist[iOff]; + int iPos = 0; + while( p->rc==SQLITE_OK ){ + int nSpace = pgsz - pBuf->n - pPgidx->n; + int n = 0; + if( (nCopy - iPos)<=nSpace ){ + n = nCopy - iPos; + }else{ + n = fts5PoslistPrefix(&pPoslist[iPos], nSpace); + } + assert( n>0 ); + fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n); + iPos += n; + if( (pBuf->n + pPgidx->n)>=pgsz ){ + fts5WriteFlushLeaf(p, &writer); + } + if( iPos>=nCopy ) break; } - assert( n>0 ); - fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n); - iPos += n; - if( (pBuf->n + pPgidx->n)>=pgsz ){ - fts5WriteFlushLeaf(p, &writer); - } - if( iPos>=nCopy ) break; } + iOff += nCopy; } - iOff += nCopy; } } - } - /* TODO2: Doclist terminator written here. */ - /* pBuf->p[pBuf->n++] = '\0'; */ - assert( pBuf->n<=pBuf->nSpace ); - if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash); - } - sqlite3Fts5HashClear(pHash); - fts5WriteFinish(p, &writer, &pgnoLast); + /* TODO2: Doclist terminator written here. */ + /* pBuf->p[pBuf->n++] = '\0'; */ + assert( pBuf->n<=pBuf->nSpace ); + if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash); + } + sqlite3Fts5HashClear(pHash); + fts5WriteFinish(p, &writer, &pgnoLast); - assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 ); - if( pgnoLast>0 ){ - /* Update the Fts5Structure. It is written back to the database by the - ** fts5StructureRelease() call below. */ - if( pStruct->nLevel==0 ){ - fts5StructureAddLevel(&p->rc, &pStruct); + assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 ); + if( pgnoLast>0 ){ + /* Update the Fts5Structure. It is written back to the database by the + ** fts5StructureRelease() call below. */ + if( pStruct->nLevel==0 ){ + fts5StructureAddLevel(&p->rc, &pStruct); + } + fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); + if( p->rc==SQLITE_OK ){ + pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; + pSeg->iSegid = iSegid; + pSeg->pgnoFirst = 1; + pSeg->pgnoLast = pgnoLast; + if( pStruct->nOriginCntr>0 ){ + pSeg->iOrigin1 = pStruct->nOriginCntr; + pSeg->iOrigin2 = pStruct->nOriginCntr; + pSeg->nEntry = p->nPendingRow; + pStruct->nOriginCntr++; + } + pStruct->nSegment++; + } + fts5StructurePromote(p, 0, pStruct); } - fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); - if( p->rc==SQLITE_OK ){ - pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; - pSeg->iSegid = iSegid; - pSeg->pgnoFirst = 1; - pSeg->pgnoLast = pgnoLast; - pStruct->nSegment++; - } - fts5StructurePromote(p, 0, pStruct); } } - fts5IndexAutomerge(p, &pStruct, pgnoLast); + fts5IndexAutomerge(p, &pStruct, pgnoLast + p->nContentlessDelete); fts5IndexCrisismerge(p, &pStruct); fts5StructureWrite(p, pStruct); fts5StructureRelease(pStruct); + p->nContentlessDelete = 0; } /* @@ -237704,10 +240154,11 @@ static void fts5FlushOneHash(Fts5Index *p){ */ static void fts5IndexFlush(Fts5Index *p){ /* Unless it is empty, flush the hash table to disk */ - if( p->nPendingData ){ + if( p->nPendingData || p->nContentlessDelete ){ assert( p->pHash ); - p->nPendingData = 0; fts5FlushOneHash(p); + p->nPendingData = 0; + p->nPendingRow = 0; } } @@ -237723,17 +240174,22 @@ static Fts5Structure *fts5IndexOptimizeStruct( /* Figure out if this structure requires optimization. A structure does ** not require optimization if either: ** - ** + it consists of fewer than two segments, or - ** + all segments are on the same level, or - ** + all segments except one are currently inputs to a merge operation. + ** 1. it consists of fewer than two segments, or + ** 2. all segments are on the same level, or + ** 3. all segments except one are currently inputs to a merge operation. ** - ** In the first case, return NULL. In the second, increment the ref-count - ** on *pStruct and return a copy of the pointer to it. + ** In the first case, if there are no tombstone hash pages, return NULL. In + ** the second, increment the ref-count on *pStruct and return a copy of the + ** pointer to it. */ - if( nSeg<2 ) return 0; + if( nSeg==0 ) return 0; for(i=0; inLevel; i++){ int nThis = pStruct->aLevel[i].nSeg; - if( nThis==nSeg || (nThis==nSeg-1 && pStruct->aLevel[i].nMerge==nThis) ){ + int nMerge = pStruct->aLevel[i].nMerge; + if( nThis>0 && (nThis==nSeg || (nThis==nSeg-1 && nMerge==nThis)) ){ + if( nSeg==1 && nThis==1 && pStruct->aLevel[i].aSeg[0].nPgTombstone==0 ){ + return 0; + } fts5StructureRef(pStruct); return pStruct; } @@ -237749,6 +240205,7 @@ static Fts5Structure *fts5IndexOptimizeStruct( pNew->nLevel = MIN(pStruct->nLevel+1, FTS5_MAX_LEVEL); pNew->nRef = 1; pNew->nWriteCounter = pStruct->nWriteCounter; + pNew->nOriginCntr = pStruct->nOriginCntr; pLvl = &pNew->aLevel[pNew->nLevel-1]; pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&p->rc, nByte); if( pLvl->aSeg ){ @@ -237779,6 +240236,7 @@ static int sqlite3Fts5IndexOptimize(Fts5Index *p){ assert( p->rc==SQLITE_OK ); fts5IndexFlush(p); + assert( p->nContentlessDelete==0 ); pStruct = fts5StructureRead(p); fts5StructureInvalidate(p); @@ -237808,7 +240266,10 @@ static int sqlite3Fts5IndexOptimize(Fts5Index *p){ ** INSERT command. */ static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){ - Fts5Structure *pStruct = fts5StructureRead(p); + Fts5Structure *pStruct = 0; + + fts5IndexFlush(p); + pStruct = fts5StructureRead(p); if( pStruct ){ int nMin = p->pConfig->nUsermerge; fts5StructureInvalidate(p); @@ -237816,7 +240277,7 @@ static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){ Fts5Structure *pNew = fts5IndexOptimizeStruct(p, pStruct); fts5StructureRelease(pStruct); pStruct = pNew; - nMin = 2; + nMin = 1; nMerge = nMerge*-1; } if( pStruct && pStruct->nLevel ){ @@ -238330,6 +240791,9 @@ static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){ p->iWriteRowid = iRowid; p->bDelete = bDelete; + if( bDelete==0 ){ + p->nPendingRow++; + } return fts5IndexReturn(p); } @@ -238367,6 +240831,9 @@ static int sqlite3Fts5IndexReinit(Fts5Index *p){ fts5StructureInvalidate(p); fts5IndexDiscardData(p); memset(&s, 0, sizeof(Fts5Structure)); + if( p->pConfig->bContentlessDelete ){ + s.nOriginCntr = 1; + } fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0); fts5StructureWrite(p, &s); return fts5IndexReturn(p); @@ -238758,6 +241225,347 @@ static int sqlite3Fts5IndexLoadConfig(Fts5Index *p){ return fts5IndexReturn(p); } +/* +** Retrieve the origin value that will be used for the segment currently +** being accumulated in the in-memory hash table when it is flushed to +** disk. If successful, SQLITE_OK is returned and (*piOrigin) set to +** the queried value. Or, if an error occurs, an error code is returned +** and the final value of (*piOrigin) is undefined. +*/ +static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin){ + Fts5Structure *pStruct; + pStruct = fts5StructureRead(p); + if( pStruct ){ + *piOrigin = pStruct->nOriginCntr; + fts5StructureRelease(pStruct); + } + return fts5IndexReturn(p); +} + +/* +** Buffer pPg contains a page of a tombstone hash table - one of nPg pages +** associated with the same segment. This function adds rowid iRowid to +** the hash table. The caller is required to guarantee that there is at +** least one free slot on the page. +** +** If parameter bForce is false and the hash table is deemed to be full +** (more than half of the slots are occupied), then non-zero is returned +** and iRowid not inserted. Or, if bForce is true or if the hash table page +** is not full, iRowid is inserted and zero returned. +*/ +static int fts5IndexTombstoneAddToPage( + Fts5Data *pPg, + int bForce, + int nPg, + u64 iRowid +){ + const int szKey = TOMBSTONE_KEYSIZE(pPg); + const int nSlot = TOMBSTONE_NSLOT(pPg); + const int nElem = fts5GetU32(&pPg->p[4]); + int iSlot = (iRowid / nPg) % nSlot; + int nCollide = nSlot; + + if( szKey==4 && iRowid>0xFFFFFFFF ) return 2; + if( iRowid==0 ){ + pPg->p[1] = 0x01; + return 0; + } + + if( bForce==0 && nElem>=(nSlot/2) ){ + return 1; + } + + fts5PutU32(&pPg->p[4], nElem+1); + if( szKey==4 ){ + u32 *aSlot = (u32*)&pPg->p[8]; + while( aSlot[iSlot] ){ + iSlot = (iSlot + 1) % nSlot; + if( nCollide--==0 ) return 0; + } + fts5PutU32((u8*)&aSlot[iSlot], (u32)iRowid); + }else{ + u64 *aSlot = (u64*)&pPg->p[8]; + while( aSlot[iSlot] ){ + iSlot = (iSlot + 1) % nSlot; + if( nCollide--==0 ) return 0; + } + fts5PutU64((u8*)&aSlot[iSlot], iRowid); + } + + return 0; +} + +/* +** This function attempts to build a new hash containing all the keys +** currently in the tombstone hash table for segment pSeg. The new +** hash will be stored in the nOut buffers passed in array apOut[]. +** All pages of the new hash use key-size szKey (4 or 8). +** +** Return 0 if the hash is successfully rebuilt into the nOut pages. +** Or non-zero if it is not (because one page became overfull). In this +** case the caller should retry with a larger nOut parameter. +** +** Parameter pData1 is page iPg1 of the hash table being rebuilt. +*/ +static int fts5IndexTombstoneRehash( + Fts5Index *p, + Fts5StructureSegment *pSeg, /* Segment to rebuild hash of */ + Fts5Data *pData1, /* One page of current hash - or NULL */ + int iPg1, /* Which page of the current hash is pData1 */ + int szKey, /* 4 or 8, the keysize */ + int nOut, /* Number of output pages */ + Fts5Data **apOut /* Array of output hash pages */ +){ + int ii; + int res = 0; + + /* Initialize the headers of all the output pages */ + for(ii=0; iip[0] = szKey; + fts5PutU32(&apOut[ii]->p[4], 0); + } + + /* Loop through the current pages of the hash table. */ + for(ii=0; res==0 && iinPgTombstone; ii++){ + Fts5Data *pData = 0; /* Page ii of the current hash table */ + Fts5Data *pFree = 0; /* Free this at the end of the loop */ + + if( iPg1==ii ){ + pData = pData1; + }else{ + pFree = pData = fts5DataRead(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid, ii)); + } + + if( pData ){ + int szKeyIn = TOMBSTONE_KEYSIZE(pData); + int nSlotIn = (pData->nn - 8) / szKeyIn; + int iIn; + for(iIn=0; iInp[8]; + if( aSlot[iIn] ) iVal = fts5GetU32((u8*)&aSlot[iIn]); + }else{ + u64 *aSlot = (u64*)&pData->p[8]; + if( aSlot[iIn] ) iVal = fts5GetU64((u8*)&aSlot[iIn]); + } + + /* If iVal is not 0 at this point, insert it into the new hash table */ + if( iVal ){ + Fts5Data *pPg = apOut[(iVal % nOut)]; + res = fts5IndexTombstoneAddToPage(pPg, 0, nOut, iVal); + if( res ) break; + } + } + + /* If this is page 0 of the old hash, copy the rowid-0-flag from the + ** old hash to the new. */ + if( ii==0 ){ + apOut[0]->p[1] = pData->p[1]; + } + } + fts5DataRelease(pFree); + } + + return res; +} + +/* +** This is called to rebuild the hash table belonging to segment pSeg. +** If parameter pData1 is not NULL, then one page of the existing hash table +** has already been loaded - pData1, which is page iPg1. The key-size for +** the new hash table is szKey (4 or 8). +** +** If successful, the new hash table is not written to disk. Instead, +** output parameter (*pnOut) is set to the number of pages in the new +** hash table, and (*papOut) to point to an array of buffers containing +** the new page data. +** +** If an error occurs, an error code is left in the Fts5Index object and +** both output parameters set to 0 before returning. +*/ +static void fts5IndexTombstoneRebuild( + Fts5Index *p, + Fts5StructureSegment *pSeg, /* Segment to rebuild hash of */ + Fts5Data *pData1, /* One page of current hash - or NULL */ + int iPg1, /* Which page of the current hash is pData1 */ + int szKey, /* 4 or 8, the keysize */ + int *pnOut, /* OUT: Number of output pages */ + Fts5Data ***papOut /* OUT: Output hash pages */ +){ + const int MINSLOT = 32; + int nSlotPerPage = MAX(MINSLOT, (p->pConfig->pgsz - 8) / szKey); + int nSlot = 0; /* Number of slots in each output page */ + int nOut = 0; + + /* Figure out how many output pages (nOut) and how many slots per + ** page (nSlot). There are three possibilities: + ** + ** 1. The hash table does not yet exist. In this case the new hash + ** table will consist of a single page with MINSLOT slots. + ** + ** 2. The hash table exists but is currently a single page. In this + ** case an attempt is made to grow the page to accommodate the new + ** entry. The page is allowed to grow up to nSlotPerPage (see above) + ** slots. + ** + ** 3. The hash table already consists of more than one page, or of + ** a single page already so large that it cannot be grown. In this + ** case the new hash consists of (nPg*2+1) pages of nSlotPerPage + ** slots each, where nPg is the current number of pages in the + ** hash table. + */ + if( pSeg->nPgTombstone==0 ){ + /* Case 1. */ + nOut = 1; + nSlot = MINSLOT; + }else if( pSeg->nPgTombstone==1 ){ + /* Case 2. */ + int nElem = (int)fts5GetU32(&pData1->p[4]); + assert( pData1 && iPg1==0 ); + nOut = 1; + nSlot = MAX(nElem*4, MINSLOT); + if( nSlot>nSlotPerPage ) nOut = 0; + } + if( nOut==0 ){ + /* Case 3. */ + nOut = (pSeg->nPgTombstone * 2 + 1); + nSlot = nSlotPerPage; + } + + /* Allocate the required array and output pages */ + while( 1 ){ + int res = 0; + int ii = 0; + int szPage = 0; + Fts5Data **apOut = 0; + + /* Allocate space for the new hash table */ + assert( nSlot>=MINSLOT ); + apOut = (Fts5Data**)sqlite3Fts5MallocZero(&p->rc, sizeof(Fts5Data*) * nOut); + szPage = 8 + nSlot*szKey; + for(ii=0; iirc, + sizeof(Fts5Data)+szPage + ); + if( pNew ){ + pNew->nn = szPage; + pNew->p = (u8*)&pNew[1]; + apOut[ii] = pNew; + } + } + + /* Rebuild the hash table. */ + if( p->rc==SQLITE_OK ){ + res = fts5IndexTombstoneRehash(p, pSeg, pData1, iPg1, szKey, nOut, apOut); + } + if( res==0 ){ + if( p->rc ){ + fts5IndexFreeArray(apOut, nOut); + apOut = 0; + nOut = 0; + } + *pnOut = nOut; + *papOut = apOut; + break; + } + + /* If control flows to here, it was not possible to rebuild the hash + ** table. Free all buffers and then try again with more pages. */ + assert( p->rc==SQLITE_OK ); + fts5IndexFreeArray(apOut, nOut); + nSlot = nSlotPerPage; + nOut = nOut*2 + 1; + } +} + + +/* +** Add a tombstone for rowid iRowid to segment pSeg. +*/ +static void fts5IndexTombstoneAdd( + Fts5Index *p, + Fts5StructureSegment *pSeg, + u64 iRowid +){ + Fts5Data *pPg = 0; + int iPg = -1; + int szKey = 0; + int nHash = 0; + Fts5Data **apHash = 0; + + p->nContentlessDelete++; + + if( pSeg->nPgTombstone>0 ){ + iPg = iRowid % pSeg->nPgTombstone; + pPg = fts5DataRead(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid,iPg)); + if( pPg==0 ){ + assert( p->rc!=SQLITE_OK ); + return; + } + + if( 0==fts5IndexTombstoneAddToPage(pPg, 0, pSeg->nPgTombstone, iRowid) ){ + fts5DataWrite(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid,iPg), pPg->p, pPg->nn); + fts5DataRelease(pPg); + return; + } + } + + /* Have to rebuild the hash table. First figure out the key-size (4 or 8). */ + szKey = pPg ? TOMBSTONE_KEYSIZE(pPg) : 4; + if( iRowid>0xFFFFFFFF ) szKey = 8; + + /* Rebuild the hash table */ + fts5IndexTombstoneRebuild(p, pSeg, pPg, iPg, szKey, &nHash, &apHash); + assert( p->rc==SQLITE_OK || (nHash==0 && apHash==0) ); + + /* If all has succeeded, write the new rowid into one of the new hash + ** table pages, then write them all out to disk. */ + if( nHash ){ + int ii = 0; + fts5IndexTombstoneAddToPage(apHash[iRowid % nHash], 1, nHash, iRowid); + for(ii=0; iiiSegid, ii); + fts5DataWrite(p, iTombstoneRowid, apHash[ii]->p, apHash[ii]->nn); + } + pSeg->nPgTombstone = nHash; + fts5StructureWrite(p, p->pStruct); + } + + fts5DataRelease(pPg); + fts5IndexFreeArray(apHash, nHash); +} + +/* +** Add iRowid to the tombstone list of the segment or segments that contain +** rows from origin iOrigin. Return SQLITE_OK if successful, or an SQLite +** error code otherwise. +*/ +static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid){ + Fts5Structure *pStruct; + pStruct = fts5StructureRead(p); + if( pStruct ){ + int bFound = 0; /* True after pSeg->nEntryTombstone incr. */ + int iLvl; + for(iLvl=pStruct->nLevel-1; iLvl>=0; iLvl--){ + int iSeg; + for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){ + Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; + if( pSeg->iOrigin1<=(u64)iOrigin && pSeg->iOrigin2>=(u64)iOrigin ){ + if( bFound==0 ){ + pSeg->nEntryTombstone++; + bFound = 1; + } + fts5IndexTombstoneAdd(p, pSeg, iRowid); + } + } + } + fts5StructureRelease(pStruct); + } + return fts5IndexReturn(p); +} /************************************************************************* ************************************************************************** @@ -239309,13 +242117,14 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum ** function only. */ -#ifdef SQLITE_TEST +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) /* ** Decode a segment-data rowid from the %_data table. This function is ** the opposite of macro FTS5_SEGMENT_ROWID(). */ static void fts5DecodeRowid( i64 iRowid, /* Rowid from %_data table */ + int *pbTombstone, /* OUT: Tombstone hash flag */ int *piSegid, /* OUT: Segment id */ int *pbDlidx, /* OUT: Dlidx flag */ int *piHeight, /* OUT: Height */ @@ -239331,13 +242140,16 @@ static void fts5DecodeRowid( iRowid >>= FTS5_DATA_DLI_B; *piSegid = (int)(iRowid & (((i64)1 << FTS5_DATA_ID_B) - 1)); -} -#endif /* SQLITE_TEST */ + iRowid >>= FTS5_DATA_ID_B; -#ifdef SQLITE_TEST + *pbTombstone = (int)(iRowid & 0x0001); +} +#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ + +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ - int iSegid, iHeight, iPgno, bDlidx; /* Rowid compenents */ - fts5DecodeRowid(iKey, &iSegid, &bDlidx, &iHeight, &iPgno); + int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid compenents */ + fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); if( iSegid==0 ){ if( iKey==FTS5_AVERAGES_ROWID ){ @@ -239347,14 +242159,16 @@ static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ } } else{ - sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{%ssegid=%d h=%d pgno=%d}", - bDlidx ? "dlidx " : "", iSegid, iHeight, iPgno + sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{%s%ssegid=%d h=%d pgno=%d}", + bDlidx ? "dlidx " : "", + bTomb ? "tombstone " : "", + iSegid, iHeight, iPgno ); } } -#endif /* SQLITE_TEST */ +#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ -#ifdef SQLITE_TEST +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) static void fts5DebugStructure( int *pRc, /* IN/OUT: error code */ Fts5Buffer *pBuf, @@ -239369,16 +242183,22 @@ static void fts5DebugStructure( ); for(iSeg=0; iSegnSeg; iSeg++){ Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; - sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " {id=%d leaves=%d..%d}", + sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " {id=%d leaves=%d..%d", pSeg->iSegid, pSeg->pgnoFirst, pSeg->pgnoLast ); + if( pSeg->iOrigin1>0 ){ + sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " origin=%lld..%lld", + pSeg->iOrigin1, pSeg->iOrigin2 + ); + } + sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}"); } sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}"); } } -#endif /* SQLITE_TEST */ +#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ -#ifdef SQLITE_TEST +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) /* ** This is part of the fts5_decode() debugging aid. ** @@ -239403,9 +242223,9 @@ static void fts5DecodeStructure( fts5DebugStructure(pRc, pBuf, p); fts5StructureRelease(p); } -#endif /* SQLITE_TEST */ +#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ -#ifdef SQLITE_TEST +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) /* ** This is part of the fts5_decode() debugging aid. ** @@ -239428,9 +242248,9 @@ static void fts5DecodeAverages( zSpace = " "; } } -#endif /* SQLITE_TEST */ +#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ -#ifdef SQLITE_TEST +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) /* ** Buffer (a/n) is assumed to contain a list of serialized varints. Read ** each varint and append its string representation to buffer pBuf. Return @@ -239447,9 +242267,9 @@ static int fts5DecodePoslist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){ } return iOff; } -#endif /* SQLITE_TEST */ +#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ -#ifdef SQLITE_TEST +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) /* ** The start of buffer (a/n) contains the start of a doclist. The doclist ** may or may not finish within the buffer. This function appends a text @@ -239482,9 +242302,9 @@ static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){ return iOff; } -#endif /* SQLITE_TEST */ +#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ -#ifdef SQLITE_TEST +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) /* ** This function is part of the fts5_decode() debugging function. It is ** only ever used with detail=none tables. @@ -239525,9 +242345,9 @@ static void fts5DecodeRowidList( sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp); } } -#endif /* SQLITE_TEST */ +#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ -#ifdef SQLITE_TEST +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) /* ** The implementation of user-defined scalar function fts5_decode(). */ @@ -239538,6 +242358,7 @@ static void fts5DecodeFunction( ){ i64 iRowid; /* Rowid for record being decoded */ int iSegid,iHeight,iPgno,bDlidx;/* Rowid components */ + int bTomb; const u8 *aBlob; int n; /* Record to decode */ u8 *a = 0; Fts5Buffer s; /* Build up text to return here */ @@ -239560,7 +242381,7 @@ static void fts5DecodeFunction( if( a==0 ) goto decode_out; if( n>0 ) memcpy(a, aBlob, n); - fts5DecodeRowid(iRowid, &iSegid, &bDlidx, &iHeight, &iPgno); + fts5DecodeRowid(iRowid, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); fts5DebugRowid(&rc, &s, iRowid); if( bDlidx ){ @@ -239579,6 +242400,28 @@ static void fts5DecodeFunction( " %d(%lld)", lvl.iLeafPgno, lvl.iRowid ); } + }else if( bTomb ){ + u32 nElem = fts5GetU32(&a[4]); + int szKey = (aBlob[0]==4 || aBlob[0]==8) ? aBlob[0] : 8; + int nSlot = (n - 8) / szKey; + int ii; + sqlite3Fts5BufferAppendPrintf(&rc, &s, " nElem=%d", (int)nElem); + if( aBlob[1] ){ + sqlite3Fts5BufferAppendPrintf(&rc, &s, " 0"); + } + for(ii=0; iiszLeaf ){ + rc = FTS5_CORRUPT; + }else{ + fts5DecodeRowidList(&rc, &s, &a[iOff], iTermOff-iOff); + } iOff = iTermOff; if( iOffestimatedCost = (double)100; + pIdxInfo->estimatedRows = 100; + pIdxInfo->idxNum = 0; + for(i=0, p=pIdxInfo->aConstraint; inConstraint; i++, p++){ + if( p->usable==0 ) continue; + if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==11 ){ + rc = SQLITE_OK; + pIdxInfo->aConstraintUsage[i].omit = 1; + pIdxInfo->aConstraintUsage[i].argvIndex = 1; + break; + } + } + return rc; +} + +/* +** This method is the destructor for bytecodevtab objects. +*/ +static int fts5structDisconnectMethod(sqlite3_vtab *pVtab){ + Fts5StructVtab *p = (Fts5StructVtab*)pVtab; + sqlite3_free(p); + return SQLITE_OK; +} + +/* +** Constructor for a new bytecodevtab_cursor object. +*/ +static int fts5structOpenMethod(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){ + int rc = SQLITE_OK; + Fts5StructVcsr *pNew = 0; + + pNew = sqlite3Fts5MallocZero(&rc, sizeof(*pNew)); + *ppCsr = (sqlite3_vtab_cursor*)pNew; + + return SQLITE_OK; +} + +/* +** Destructor for a bytecodevtab_cursor. +*/ +static int fts5structCloseMethod(sqlite3_vtab_cursor *cur){ + Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; + fts5StructureRelease(pCsr->pStruct); + sqlite3_free(pCsr); + return SQLITE_OK; +} + + +/* +** Advance a bytecodevtab_cursor to its next row of output. +*/ +static int fts5structNextMethod(sqlite3_vtab_cursor *cur){ + Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; + Fts5Structure *p = pCsr->pStruct; + + assert( pCsr->pStruct ); + pCsr->iSeg++; + pCsr->iRowid++; + while( pCsr->iLevelnLevel && pCsr->iSeg>=p->aLevel[pCsr->iLevel].nSeg ){ + pCsr->iLevel++; + pCsr->iSeg = 0; + } + if( pCsr->iLevel>=p->nLevel ){ + fts5StructureRelease(pCsr->pStruct); + pCsr->pStruct = 0; + } + return SQLITE_OK; +} + +/* +** Return TRUE if the cursor has been moved off of the last +** row of output. +*/ +static int fts5structEofMethod(sqlite3_vtab_cursor *cur){ + Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; + return pCsr->pStruct==0; +} + +static int fts5structRowidMethod( + sqlite3_vtab_cursor *cur, + sqlite_int64 *piRowid +){ + Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; + *piRowid = pCsr->iRowid; + return SQLITE_OK; +} + +/* +** Return values of columns for the row at which the bytecodevtab_cursor +** is currently pointing. +*/ +static int fts5structColumnMethod( + sqlite3_vtab_cursor *cur, /* The cursor */ + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ + int i /* Which column to return */ +){ + Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; + Fts5Structure *p = pCsr->pStruct; + Fts5StructureSegment *pSeg = &p->aLevel[pCsr->iLevel].aSeg[pCsr->iSeg]; + + switch( i ){ + case 0: /* level */ + sqlite3_result_int(ctx, pCsr->iLevel); + break; + case 1: /* segment */ + sqlite3_result_int(ctx, pCsr->iSeg); + break; + case 2: /* merge */ + sqlite3_result_int(ctx, pCsr->iSeg < p->aLevel[pCsr->iLevel].nMerge); + break; + case 3: /* segid */ + sqlite3_result_int(ctx, pSeg->iSegid); + break; + case 4: /* leaf1 */ + sqlite3_result_int(ctx, pSeg->pgnoFirst); + break; + case 5: /* leaf2 */ + sqlite3_result_int(ctx, pSeg->pgnoLast); + break; + case 6: /* origin1 */ + sqlite3_result_int64(ctx, pSeg->iOrigin1); + break; + case 7: /* origin2 */ + sqlite3_result_int64(ctx, pSeg->iOrigin2); + break; + case 8: /* npgtombstone */ + sqlite3_result_int(ctx, pSeg->nPgTombstone); + break; + case 9: /* nentrytombstone */ + sqlite3_result_int64(ctx, pSeg->nEntryTombstone); + break; + case 10: /* nentry */ + sqlite3_result_int64(ctx, pSeg->nEntry); + break; + } + return SQLITE_OK; +} + +/* +** Initialize a cursor. +** +** idxNum==0 means show all subprograms +** idxNum==1 means show only the main bytecode and omit subprograms. +*/ +static int fts5structFilterMethod( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + Fts5StructVcsr *pCsr = (Fts5StructVcsr *)pVtabCursor; + int rc = SQLITE_OK; + + const u8 *aBlob = 0; + int nBlob = 0; + + assert( argc==1 ); + fts5StructureRelease(pCsr->pStruct); + pCsr->pStruct = 0; + + nBlob = sqlite3_value_bytes(argv[0]); + aBlob = (const u8*)sqlite3_value_blob(argv[0]); + rc = fts5StructureDecode(aBlob, nBlob, 0, &pCsr->pStruct); + if( rc==SQLITE_OK ){ + pCsr->iLevel = 0; + pCsr->iRowid = 0; + pCsr->iSeg = -1; + rc = fts5structNextMethod(pVtabCursor); + } + + return rc; +} + +#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ /* ** This is called as part of registering the FTS5 module with database @@ -239783,7 +242857,7 @@ static void fts5RowidFunction( ** SQLite error code is returned instead. */ static int sqlite3Fts5IndexInit(sqlite3 *db){ -#ifdef SQLITE_TEST +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) int rc = sqlite3_create_function( db, "fts5_decode", 2, SQLITE_UTF8, 0, fts5DecodeFunction, 0, 0 ); @@ -239800,6 +242874,36 @@ static int sqlite3Fts5IndexInit(sqlite3 *db){ db, "fts5_rowid", -1, SQLITE_UTF8, 0, fts5RowidFunction, 0, 0 ); } + + if( rc==SQLITE_OK ){ + static const sqlite3_module fts5structure_module = { + 0, /* iVersion */ + 0, /* xCreate */ + fts5structConnectMethod, /* xConnect */ + fts5structBestIndexMethod, /* xBestIndex */ + fts5structDisconnectMethod, /* xDisconnect */ + 0, /* xDestroy */ + fts5structOpenMethod, /* xOpen */ + fts5structCloseMethod, /* xClose */ + fts5structFilterMethod, /* xFilter */ + fts5structNextMethod, /* xNext */ + fts5structEofMethod, /* xEof */ + fts5structColumnMethod, /* xColumn */ + fts5structRowidMethod, /* xRowid */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindFunction */ + 0, /* xRename */ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ + 0 /* xShadowName */ + }; + rc = sqlite3_create_module(db, "fts5_structure", &fts5structure_module, 0); + } return rc; #else return SQLITE_OK; @@ -241443,7 +244547,6 @@ static int fts5UpdateMethod( int rc = SQLITE_OK; /* Return code */ int bUpdateOrDelete = 0; - /* A transaction must be open when this is called. */ assert( pTab->ts.eState==1 || pTab->ts.eState==2 ); @@ -241472,7 +244575,14 @@ static int fts5UpdateMethod( if( pConfig->eContent!=FTS5_CONTENT_NORMAL && 0==sqlite3_stricmp("delete", z) ){ - rc = fts5SpecialDelete(pTab, apVal); + if( pConfig->bContentlessDelete ){ + fts5SetVtabError(pTab, + "'delete' may not be used with a contentless_delete=1 table" + ); + rc = SQLITE_ERROR; + }else{ + rc = fts5SpecialDelete(pTab, apVal); + } }else{ rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]); } @@ -241489,7 +244599,7 @@ static int fts5UpdateMethod( ** Cases 3 and 4 may violate the rowid constraint. */ int eConflict = SQLITE_ABORT; - if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ + if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->bContentlessDelete ){ eConflict = sqlite3_vtab_on_conflict(pConfig->db); } @@ -241497,8 +244607,12 @@ static int fts5UpdateMethod( assert( nArg!=1 || eType0==SQLITE_INTEGER ); /* Filter out attempts to run UPDATE or DELETE on contentless tables. - ** This is not suported. */ - if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){ + ** This is not suported. Except - DELETE is supported if the CREATE + ** VIRTUAL TABLE statement contained "contentless_delete=1". */ + if( eType0==SQLITE_INTEGER + && pConfig->eContent==FTS5_CONTENT_NONE + && pConfig->bContentlessDelete==0 + ){ pTab->p.base.zErrMsg = sqlite3_mprintf( "cannot %s contentless fts5 table: %s", (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName @@ -241585,8 +244699,7 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; fts5CheckTransactionState(pTab, FTS5_SYNC, 0); pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; - fts5TripCursors(pTab); - rc = sqlite3Fts5StorageSync(pTab->pStorage); + rc = sqlite3Fts5FlushToDisk(&pTab->p); pTab->p.pConfig->pzErrmsg = 0; return rc; } @@ -242353,6 +245466,12 @@ static int fts5ColumnMethod( sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1)); } pConfig->pzErrmsg = 0; + }else if( pConfig->bContentlessDelete && sqlite3_vtab_nochange(pCtx) ){ + char *zErr = sqlite3_mprintf("cannot UPDATE a subset of " + "columns on fts5 contentless-delete table: %s", pConfig->zName + ); + sqlite3_result_error(pCtx, zErr, -1); + sqlite3_free(zErr); } return rc; } @@ -242635,7 +245754,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2023-09-11 12:01:27 2d3a40c05c49e1a49264912b1a05bc2143ac0e7c3df588276ce80a4cbc9bd1b0", -1, SQLITE_TRANSIENT); } /* @@ -242848,10 +245967,10 @@ static int fts5StorageGetStmt( "INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */ "REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */ "DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */ - "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* REPLACE_DOCSIZE */ + "REPLACE INTO %Q.'%q_docsize' VALUES(?,?%s)", /* REPLACE_DOCSIZE */ "DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */ - "SELECT sz FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */ + "SELECT sz%s FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */ "REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */ "SELECT %s FROM %s AS T", /* SCAN */ @@ -242899,6 +246018,19 @@ static int fts5StorageGetStmt( break; } + case FTS5_STMT_REPLACE_DOCSIZE: + zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, + (pC->bContentlessDelete ? ",?" : "") + ); + break; + + case FTS5_STMT_LOOKUP_DOCSIZE: + zSql = sqlite3_mprintf(azStmt[eStmt], + (pC->bContentlessDelete ? ",origin" : ""), + pC->zDb, pC->zName + ); + break; + default: zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName); break; @@ -243088,9 +246220,11 @@ static int sqlite3Fts5StorageOpen( } if( rc==SQLITE_OK && pConfig->bColumnsize ){ - rc = sqlite3Fts5CreateTable( - pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr - ); + const char *zCols = "id INTEGER PRIMARY KEY, sz BLOB"; + if( pConfig->bContentlessDelete ){ + zCols = "id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER"; + } + rc = sqlite3Fts5CreateTable(pConfig, "docsize", zCols, 0, pzErr); } if( rc==SQLITE_OK ){ rc = sqlite3Fts5CreateTable( @@ -243167,7 +246301,7 @@ static int fts5StorageDeleteFromIndex( ){ Fts5Config *pConfig = p->pConfig; sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */ - int rc; /* Return code */ + int rc = SQLITE_OK; /* Return code */ int rc2; /* sqlite3_reset() return code */ int iCol; Fts5InsertCtx ctx; @@ -243183,7 +246317,6 @@ static int fts5StorageDeleteFromIndex( ctx.pStorage = p; ctx.iCol = -1; - rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel); for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){ if( pConfig->abUnindexed[iCol-1]==0 ){ const char *zText; @@ -243220,6 +246353,37 @@ static int fts5StorageDeleteFromIndex( return rc; } +/* +** This function is called to process a DELETE on a contentless_delete=1 +** table. It adds the tombstone required to delete the entry with rowid +** iDel. If successful, SQLITE_OK is returned. Or, if an error occurs, +** an SQLite error code. +*/ +static int fts5StorageContentlessDelete(Fts5Storage *p, i64 iDel){ + i64 iOrigin = 0; + sqlite3_stmt *pLookup = 0; + int rc = SQLITE_OK; + + assert( p->pConfig->bContentlessDelete ); + assert( p->pConfig->eContent==FTS5_CONTENT_NONE ); + + /* Look up the origin of the document in the %_docsize table. Store + ** this in stack variable iOrigin. */ + rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0); + if( rc==SQLITE_OK ){ + sqlite3_bind_int64(pLookup, 1, iDel); + if( SQLITE_ROW==sqlite3_step(pLookup) ){ + iOrigin = sqlite3_column_int64(pLookup, 1); + } + rc = sqlite3_reset(pLookup); + } + + if( rc==SQLITE_OK && iOrigin!=0 ){ + rc = sqlite3Fts5IndexContentlessDelete(p->pIndex, iOrigin, iDel); + } + + return rc; +} /* ** Insert a record into the %_docsize table. Specifically, do: @@ -243240,10 +246404,17 @@ static int fts5StorageInsertDocsize( rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0); if( rc==SQLITE_OK ){ sqlite3_bind_int64(pReplace, 1, iRowid); - sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC); - sqlite3_step(pReplace); - rc = sqlite3_reset(pReplace); - sqlite3_bind_null(pReplace, 2); + if( p->pConfig->bContentlessDelete ){ + i64 iOrigin = 0; + rc = sqlite3Fts5IndexGetOrigin(p->pIndex, &iOrigin); + sqlite3_bind_int64(pReplace, 3, iOrigin); + } + if( rc==SQLITE_OK ){ + sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC); + sqlite3_step(pReplace); + rc = sqlite3_reset(pReplace); + sqlite3_bind_null(pReplace, 2); + } } } return rc; @@ -243307,7 +246478,15 @@ static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **ap /* Delete the index records */ if( rc==SQLITE_OK ){ - rc = fts5StorageDeleteFromIndex(p, iDel, apVal); + rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel); + } + + if( rc==SQLITE_OK ){ + if( p->pConfig->bContentlessDelete ){ + rc = fts5StorageContentlessDelete(p, iDel); + }else{ + rc = fts5StorageDeleteFromIndex(p, iDel, apVal); + } } /* Delete the %_docsize record */ diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h index 48effe20216..b9d06929888 100644 --- a/src/libs/3rdparty/sqlite/sqlite3.h +++ b/src/libs/3rdparty/sqlite/sqlite3.h @@ -146,9 +146,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.42.0" -#define SQLITE_VERSION_NUMBER 3042000 -#define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0" +#define SQLITE_VERSION "3.43.1" +#define SQLITE_VERSION_NUMBER 3043001 +#define SQLITE_SOURCE_ID "2023-09-11 12:01:27 2d3a40c05c49e1a49264912b1a05bc2143ac0e7c3df588276ce80a4cbc9bd1b0" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -528,6 +528,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) +#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) @@ -1190,7 +1191,7 @@ struct sqlite3_io_methods { ** by clients within the current process, only within other processes. ** **
    4. [[SQLITE_FCNTL_CKSM_FILE]] -** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use interally by the +** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use internally by the ** [checksum VFS shim] only. ** **
    5. [[SQLITE_FCNTL_RESET_CACHE]] @@ -2454,7 +2455,7 @@ struct sqlite3_mem_methods { ** the [VACUUM] command will fail with an obscure error when attempting to ** process a table with generated columns and a descending index. This is ** not considered a bug since SQLite versions 3.3.0 and earlier do not support -** either generated columns or decending indexes. +** either generated columns or descending indexes. ** ** ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] @@ -2735,6 +2736,7 @@ SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*); ** ** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether ** or not an interrupt is currently in effect for [database connection] D. +** It returns 1 if an interrupt is currently in effect, or 0 otherwise. */ SQLITE_API void sqlite3_interrupt(sqlite3*); SQLITE_API int sqlite3_is_interrupted(sqlite3*); @@ -3388,8 +3390,10 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, ** M argument should be the bitwise OR-ed combination of ** zero or more [SQLITE_TRACE] constants. ** -** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides -** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2(). +** ^Each call to either sqlite3_trace(D,X,P) or sqlite3_trace_v2(D,M,X,P) +** overrides (cancels) all prior calls to sqlite3_trace(D,X,P) or +** sqlite3_trace_v2(D,M,X,P) for the [database connection] D. Each +** database connection may have at most one trace callback. ** ** ^The X callback is invoked whenever any of the events identified by ** mask M occur. ^The integer return value from the callback is currently @@ -3758,7 +3762,7 @@ SQLITE_API int sqlite3_open_v2( ** as F) must be one of: **
        **
      • A database filename pointer created by the SQLite core and -** passed into the xOpen() method of a VFS implemention, or +** passed into the xOpen() method of a VFS implementation, or **
      • A filename obtained from [sqlite3_db_filename()], or **
      • A new filename constructed using [sqlite3_create_filename()]. **
      @@ -3871,7 +3875,7 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); /* ** CAPI3REF: Create and Destroy VFS Filenames ** -** These interfces are provided for use by [VFS shim] implementations and +** These interfaces are provided for use by [VFS shim] implementations and ** are not useful outside of that context. ** ** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of @@ -4418,6 +4422,41 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); */ SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); +/* +** CAPI3REF: Change The EXPLAIN Setting For A Prepared Statement +** METHOD: sqlite3_stmt +** +** The sqlite3_stmt_explain(S,E) interface changes the EXPLAIN +** setting for [prepared statement] S. If E is zero, then S becomes +** a normal prepared statement. If E is 1, then S behaves as if +** its SQL text began with "[EXPLAIN]". If E is 2, then S behaves as if +** its SQL text began with "[EXPLAIN QUERY PLAN]". +** +** Calling sqlite3_stmt_explain(S,E) might cause S to be reprepared. +** SQLite tries to avoid a reprepare, but a reprepare might be necessary +** on the first transition into EXPLAIN or EXPLAIN QUERY PLAN mode. +** +** Because of the potential need to reprepare, a call to +** sqlite3_stmt_explain(S,E) will fail with SQLITE_ERROR if S cannot be +** reprepared because it was created using [sqlite3_prepare()] instead of +** the newer [sqlite3_prepare_v2()] or [sqlite3_prepare_v3()] interfaces and +** hence has no saved SQL text with which to reprepare. +** +** Changing the explain setting for a prepared statement does not change +** the original SQL text for the statement. Hence, if the SQL text originally +** began with EXPLAIN or EXPLAIN QUERY PLAN, but sqlite3_stmt_explain(S,0) +** is called to convert the statement into an ordinary statement, the EXPLAIN +** or EXPLAIN QUERY PLAN keywords will still appear in the sqlite3_sql(S) +** output, even though the statement now acts like a normal SQL statement. +** +** This routine returns SQLITE_OK if the explain mode is successfully +** changed, or an error code if the explain mode could not be changed. +** The explain mode cannot be changed while a statement is active. +** Hence, it is good practice to call [sqlite3_reset(S)] +** immediately prior to calling sqlite3_stmt_explain(S,E). +*/ +SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode); + /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt @@ -4581,7 +4620,7 @@ typedef struct sqlite3_context sqlite3_context; ** with it may be passed. ^It is called to dispose of the BLOB or string even ** if the call to the bind API fails, except the destructor is not called if ** the third parameter is a NULL pointer or the fourth parameter is negative. -** ^ (2) The special constant, [SQLITE_STATIC], may be passsed to indicate that +** ^ (2) The special constant, [SQLITE_STATIC], may be passed to indicate that ** the application remains responsible for disposing of the object. ^In this ** case, the object and the provided pointer to it must remain valid until ** either the prepared statement is finalized or the same SQL parameter is @@ -5260,14 +5299,26 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); ** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S ** back to the beginning of its program. ** -** ^If the most recent call to [sqlite3_step(S)] for the -** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], -** or if [sqlite3_step(S)] has never before been called on S, -** then [sqlite3_reset(S)] returns [SQLITE_OK]. +** ^The return code from [sqlite3_reset(S)] indicates whether or not +** the previous evaluation of prepared statement S completed successfully. +** ^If [sqlite3_step(S)] has never before been called on S or if +** [sqlite3_step(S)] has not been called since the previous call +** to [sqlite3_reset(S)], then [sqlite3_reset(S)] will return +** [SQLITE_OK]. ** ** ^If the most recent call to [sqlite3_step(S)] for the ** [prepared statement] S indicated an error, then ** [sqlite3_reset(S)] returns an appropriate [error code]. +** ^The [sqlite3_reset(S)] interface might also return an [error code] +** if there were no prior errors but the process of resetting +** the prepared statement caused a new error. ^For example, if an +** [INSERT] statement with a [RETURNING] clause is only stepped one time, +** that one call to [sqlite3_step(S)] might return SQLITE_ROW but +** the overall statement might still fail and the [sqlite3_reset(S)] call +** might return SQLITE_BUSY if locking constraints prevent the +** database change from committing. Therefore, it is important that +** applications check the return code from [sqlite3_reset(S)] even if +** no prior call to [sqlite3_step(S)] indicated a problem. ** ** ^The [sqlite3_reset(S)] interface does not change the values ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. @@ -5484,7 +5535,7 @@ SQLITE_API int sqlite3_create_window_function( ** [application-defined SQL function] ** that has side-effects or that could potentially leak sensitive information. ** This will prevent attacks in which an application is tricked -** into using a database file that has had its schema surreptiously +** into using a database file that has had its schema surreptitiously ** modified to invoke the application-defined function in ways that are ** harmful. **

      @@ -8161,7 +8212,8 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_TRACEFLAGS 31 #define SQLITE_TESTCTRL_TUNE 32 #define SQLITE_TESTCTRL_LOGEST 33 -#define SQLITE_TESTCTRL_LAST 33 /* Largest TESTCTRL */ +#define SQLITE_TESTCTRL_USELONGDOUBLE 34 +#define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking @@ -9193,8 +9245,8 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing -** unlock-notify callback is canceled. ^The blocked connections -** unlock-notify callback may also be canceled by closing the blocked +** unlock-notify callback is cancelled. ^The blocked connections +** unlock-notify callback may also be cancelled by closing the blocked ** connection using [sqlite3_close()]. ** ** The unlock-notify callback is not reentrant. If an application invokes @@ -9617,7 +9669,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); ** [[SQLITE_VTAB_DIRECTONLY]]

      SQLITE_VTAB_DIRECTONLY
      **
      Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_DIRECTONLY) from within the -** the [xConnect] or [xCreate] methods of a [virtual table] implmentation +** the [xConnect] or [xCreate] methods of a [virtual table] implementation ** prohibits that virtual table from being used from within triggers and ** views. **
      @@ -9807,7 +9859,7 @@ SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*); ** communicated to the xBestIndex method as a ** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use ** this constraint, it must set the corresponding -** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under +** aConstraintUsage[].argvIndex to a positive integer. ^(Then, under ** the usual mode of handling IN operators, SQLite generates [bytecode] ** that invokes the [xFilter|xFilter() method] once for each value ** on the right-hand side of the IN operator.)^ Thus the virtual table @@ -10236,7 +10288,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); ** When the [sqlite3_blob_write()] API is used to update a blob column, ** the pre-update hook is invoked with SQLITE_DELETE. This is because the ** in this case the new values are not available. In this case, when a -** callback made with op==SQLITE_DELETE is actuall a write using the +** callback made with op==SQLITE_DELETE is actually a write using the ** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns ** the index of the column being written. In other cases, where the ** pre-update hook is being invoked for some other reason, including a @@ -12754,7 +12806,7 @@ struct Fts5PhraseIter { ** See xPhraseFirstColumn above. */ struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 3 */ + int iVersion; /* Currently always set to 2 */ void *(*xUserData)(Fts5Context*); @@ -12983,8 +13035,8 @@ struct Fts5ExtensionApi { ** as separate queries of the FTS index are required for each synonym. ** ** When using methods (2) or (3), it is important that the tokenizer only -** provide synonyms when tokenizing document text (method (2)) or query -** text (method (3)), not both. Doing so will not cause any errors, but is +** provide synonyms when tokenizing document text (method (3)) or query +** text (method (2)), not both. Doing so will not cause any errors, but is ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; @@ -13032,7 +13084,7 @@ struct fts5_api { int (*xCreateTokenizer)( fts5_api *pApi, const char *zName, - void *pContext, + void *pUserData, fts5_tokenizer *pTokenizer, void (*xDestroy)(void*) ); @@ -13041,7 +13093,7 @@ struct fts5_api { int (*xFindTokenizer)( fts5_api *pApi, const char *zName, - void **ppContext, + void **ppUserData, fts5_tokenizer *pTokenizer ); @@ -13049,7 +13101,7 @@ struct fts5_api { int (*xCreateFunction)( fts5_api *pApi, const char *zName, - void *pContext, + void *pUserData, fts5_extension_function xFunction, void (*xDestroy)(void*) ); diff --git a/src/libs/3rdparty/sqlite/sqlite3ext.h b/src/libs/3rdparty/sqlite/sqlite3ext.h index 19e030028ad..71163809926 100644 --- a/src/libs/3rdparty/sqlite/sqlite3ext.h +++ b/src/libs/3rdparty/sqlite/sqlite3ext.h @@ -361,6 +361,8 @@ struct sqlite3_api_routines { int (*value_encoding)(sqlite3_value*); /* Version 3.41.0 and later */ int (*is_interrupted)(sqlite3*); + /* Version 3.43.0 and later */ + int (*stmt_explain)(sqlite3_stmt*,int); }; /* @@ -689,6 +691,8 @@ typedef int (*sqlite3_loadext_entry)( #define sqlite3_value_encoding sqlite3_api->value_encoding /* Version 3.41.0 and later */ #define sqlite3_is_interrupted sqlite3_api->is_interrupted +/* Version 3.43.0 and later */ +#define sqlite3_stmt_explain sqlite3_api->stmt_explain #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) From 0ad9b6fb3ac882add00ccfddb2aa40af99a26ce2 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 1 Oct 2023 01:00:33 +0200 Subject: [PATCH 115/130] Utils: Modernize SmallString std::to_chars should now be supported by all compilers. The assert is not needed because there are already tests for the extreme cases. It is anyway bad style to have asserts in code under tests. Change-Id: I5dbfa462168766e40fffa1f1780a167f1f37e72a Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tim Jenssen Reviewed-by: Orgad Shaneh --- src/libs/utils/smallstring.h | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index b28e0c76f6f..54318795a71 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -560,30 +560,20 @@ public: static BasicSmallString number(int number) { -#ifdef __cpp_lib_to_chars - // +1 for the sign, +1 for the extra digit + // 2 bytes for the sign and because digits10 returns the floor char buffer[std::numeric_limits::digits10 + 2]; auto result = std::to_chars(buffer, buffer + sizeof(buffer), number, 10); - Q_ASSERT(result.ec == std::errc{}); auto endOfConversionString = result.ptr; return BasicSmallString(buffer, endOfConversionString); -#else - return std::to_string(number); -#endif } static BasicSmallString number(long long int number) noexcept { -#ifdef __cpp_lib_to_chars - // +1 for the sign, +1 for the extra digit + // 2 bytes for the sign and because digits10 returns the floor char buffer[std::numeric_limits::digits10 + 2]; auto result = std::to_chars(buffer, buffer + sizeof(buffer), number, 10); - Q_ASSERT(result.ec == std::errc{}); auto endOfConversionString = result.ptr; return BasicSmallString(buffer, endOfConversionString); -#else - return std::to_string(number); -#endif } static BasicSmallString number(double number) noexcept { return std::to_string(number); } From 84bbfec651b11536d72f05cbf964c16b820d1606 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Tue, 3 Oct 2023 10:34:50 +0300 Subject: [PATCH 116/130] QmlDesginer: Add functionality effect preview Also some refactoring and fixes to the model Task-number: QDS-10811 Change-Id: If27b2e8e9bac4d46200ed37bf0aec8255ee19022 Reviewed-by: Mahmoud Badri --- .../EffectMakerPreview.qml | 16 +++++++-- .../effectmakernew/effectmakermodel.cpp | 34 ++++++++++++++++--- src/plugins/effectmakernew/effectmakermodel.h | 10 ++++++ 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index f2b352ba4d1..c23e95e5628 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -17,6 +17,8 @@ Column { required property Item mainRoot property var effectMakerModel: EffectMakerBackend.effectMakerModel property alias source: source + // The delay in ms to wait until updating the effect + readonly property int updateDelay: 200 Rectangle { // toolbar width: parent.width @@ -160,7 +162,7 @@ Column { try { const newObject = Qt.createQmlObject( - effectMakerModel.qmlComponentString, //TODO + effectMakerModel.qmlComponentString, componentParent, "" ); @@ -178,11 +180,19 @@ Column { } } + Connections { + target: effectMakerModel + function onShadersBaked() { + console.log("Shaders Baked!") + //updateTimer.restart(); // Disable for now + } + } + Timer { id: updateTimer - interval: effectMakerModel.effectUpdateDelay(); //TODO + interval: updateDelay; onTriggered: { - effectMakerModel.updateQmlComponent(); //TODO + effectMakerModel.updateQmlComponent(); createNewComponent(); } } diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 8b1836a5afa..9f86025207a 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -104,6 +104,8 @@ void EffectMakerModel::addNode(const QString &nodeQenPath) endInsertRows(); setIsEmpty(false); + + bakeShaders(); } void EffectMakerModel::moveNode(int fromIdx, int toIdx) @@ -115,6 +117,8 @@ void EffectMakerModel::moveNode(int fromIdx, int toIdx) beginMoveRows({}, fromIdx, fromIdx, {}, toIdxAdjusted); m_nodes.move(fromIdx, toIdx); endMoveRows(); + + bakeShaders(); } void EffectMakerModel::removeNode(int idx) @@ -127,6 +131,8 @@ void EffectMakerModel::removeNode(int idx) if (m_nodes.isEmpty()) setIsEmpty(true); + else + bakeShaders(); } void EffectMakerModel::updateBakedShaderVersions() @@ -170,6 +176,11 @@ void EffectMakerModel::setVertexShader(const QString &newVertexShader) m_vertexShader = newVertexShader; } +const QString &EffectMakerModel::qmlComponentString() const +{ + return m_qmlComponentString; +} + const QList EffectMakerModel::allUniforms() { QList uniforms = {}; @@ -601,7 +612,7 @@ QString EffectMakerModel::generateVertexShader(bool includeUniforms) const bool removeTags = includeUniforms; s += getDefineProperties(); - s += getConstVariables(); + // s += getConstVariables(); // Not sure yet, will check on this later // When the node is complete, add shader code in correct nodes order // split to root and main parts @@ -661,7 +672,7 @@ QString EffectMakerModel::generateFragmentShader(bool includeUniforms) const bool removeTags = includeUniforms; s += getDefineProperties(); - s += getConstVariables(); + // s += getConstVariables(); // Not sure yet, will check on this later // When the node is complete, add shader code in correct nodes order // split to root and main parts @@ -787,14 +798,14 @@ void EffectMakerModel::bakeShaders() // First update the features based on shader content // This will make sure that next calls to "generate" will produce correct uniforms. - m_shaderFeatures.update(generateVertexShader(false), generateFragmentShader(false), m_previewEffectPropertiesString); + m_shaderFeatures.update(generateVertexShader(false), generateFragmentShader(false), + m_previewEffectPropertiesString); updateCustomUniforms(); setVertexShader(generateVertexShader()); QString vs = m_vertexShader; m_baker.setSourceString(vs.toUtf8(), QShader::VertexStage); - QShader vertShader = m_baker.bake(); if (!vertShader.isValid()) { qWarning() << "Shader baking failed:" << qPrintable(m_baker.errorMessage()); @@ -840,5 +851,20 @@ void EffectMakerModel::setShadersUpToDate(bool UpToDate) emit shadersUpToDateChanged(); } +QString EffectMakerModel::getQmlComponentString(bool localFiles) +{ + Q_UNUSED(localFiles) + + // TODO + return QString(); +} + +void EffectMakerModel::updateQmlComponent() +{ + // Clear possible QML runtime errors + resetEffectError(ErrorQMLRuntime); + m_qmlComponentString = getQmlComponentString(false); +} + } // namespace EffectMaker diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index aa418df303e..af093de1814 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -36,6 +36,8 @@ class EffectMakerModel : public QAbstractListModel Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) Q_PROPERTY(bool shadersUpToDate READ shadersUpToDate WRITE setShadersUpToDate NOTIFY shadersUpToDateChanged) + Q_PROPERTY(QString qmlComponentString READ qmlComponentString) + public: EffectMakerModel(QObject *parent = nullptr); @@ -61,6 +63,11 @@ public: QString vertexShader() const; void setVertexShader(const QString &newVertexShader); + const QString &qmlComponentString() const; + void setQmlComponentString(const QString &string); + + Q_INVOKABLE void updateQmlComponent(); + signals: void isEmptyChanged(); void selectedIndexChanged(int idx); @@ -117,6 +124,8 @@ private: void updateCustomUniforms(); void bakeShaders(); + QString getQmlComponentString(bool localFiles); + QList m_nodes; int m_selectedIndex = -1; @@ -139,6 +148,7 @@ private: QString m_exportedEffectPropertiesString; // Used in preview QML, at ShaderEffect component of the file QString m_previewEffectPropertiesString; + QString m_qmlComponentString; const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); }; From d08b4bcb310c4c4f7dd669947d1da3e6ae879d6c Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 2 Oct 2023 17:57:00 +0300 Subject: [PATCH 117/130] QmlDesigner: Fix snap drag issues Dragging to exactly (0,0,0) value wasn't registered as the null vector got discarded when splitting it into subproperties. Scale snap was snapping always to higher interval when new scale was less than one, so it would never snap to zero. Fixes: QDS-10652 Change-Id: I65135c258feeb10b9b17ea621e4d65c1d97e1794 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp | 4 ++-- .../qml2puppet/instances/qt5informationnodeinstanceserver.cpp | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 6f419fdf410..f1b4b795e47 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -965,7 +965,7 @@ double GeneralHelper::adjustRotationForSnap(double newAngle) static double adjustScaler(double newScale, double increment) { double absScale = qAbs(newScale); - double comp1 = 1. + double(int((absScale / increment) - (1. / increment))) * increment; + double comp1 = 1. + double(int(int(absScale / increment) - (1. / increment))) * increment; double comp2 = comp1 + increment; double retVal = absScale - comp1 > comp2 - absScale ? comp2 : comp1; if (newScale < 0) @@ -978,7 +978,7 @@ double GeneralHelper::adjustScalerForSnap(double newScale) bool snapScale = m_snapScale; double increment = m_snapScaleInterval; - if (qFuzzyIsNull(newScale) || !queryKeyboardForSnapping(snapScale, increment)) + if (!queryKeyboardForSnapping(snapScale, increment)) return newScale; return adjustScaler(newScale, increment); diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index d05de5635b0..bff0732d0c1 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -729,10 +729,6 @@ Qt5InformationNodeInstanceServer::propertyToPropertyValueTriples( if (variant.typeId() == QVariant::Vector3D) { auto vector3d = variant.value(); - - if (vector3d.isNull()) - return result; - const PropertyName dot = propertyName.isEmpty() ? "" : "."; propTriple.instance = instance; propTriple.propertyName = propertyName + dot + PropertyName("x"); From 340a1c115696bb73a8e6198a4d0266c9ee2d20aa Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Tue, 3 Oct 2023 16:00:01 +0300 Subject: [PATCH 118/130] QmlDesigner: Generate the effect qml code Task-number: QDS-10811 Change-Id: I5f0e969ee21ae49580c2632da483f5701dab9c41 Reviewed-by: Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../effectmakernew/effectmakermodel.cpp | 68 ++++++++++++++++++- src/plugins/effectmakernew/effectmakermodel.h | 1 + src/plugins/effectmakernew/shaderfeatures.cpp | 10 +++ src/plugins/effectmakernew/shaderfeatures.h | 4 ++ 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 9f86025207a..9da7d97ef60 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -851,7 +851,7 @@ void EffectMakerModel::setShadersUpToDate(bool UpToDate) emit shadersUpToDateChanged(); } -QString EffectMakerModel::getQmlComponentString(bool localFiles) +QString EffectMakerModel::getQmlImagesString(bool localFiles) { Q_UNUSED(localFiles) @@ -859,6 +859,72 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) return QString(); } +QString EffectMakerModel::getQmlComponentString(bool localFiles) +{ + auto addProperty = [localFiles](const QString &name, const QString &var, + const QString &type, bool blurHelper = false) + { + if (localFiles) { + const QString parent = blurHelper ? QString("blurHelper.") : QString("rootItem."); + return QString("readonly property alias %1: %2%3\n").arg(name, parent, var); + } else { + const QString parent = blurHelper ? "blurHelper." : QString(); + return QString("readonly property %1 %2: %3%4\n").arg(type, name, parent, var); + } + }; + + QString customImagesString = getQmlImagesString(localFiles); + QString vertexShaderFilename = "file:///" + m_fragmentShaderFile.fileName(); + QString fragmentShaderFilename = "file:///" + m_vertexShaderFile.fileName(); + QString s; + QString l1 = localFiles ? QStringLiteral(" ") : QStringLiteral(""); + QString l2 = localFiles ? QStringLiteral(" ") : QStringLiteral(" "); + QString l3 = localFiles ? QStringLiteral(" ") : QStringLiteral(" "); + + if (!localFiles) + s += "import QtQuick\n"; + s += l1 + "ShaderEffect {\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::Source)) + s += l2 + addProperty("iSource", "source", "Item"); + if (m_shaderFeatures.enabled(ShaderFeatures::Time)) + s += l2 + addProperty("iTime", "animatedTime", "real"); + if (m_shaderFeatures.enabled(ShaderFeatures::Frame)) + s += l2 + addProperty("iFrame", "animatedFrame", "int"); + if (m_shaderFeatures.enabled(ShaderFeatures::Resolution)) { + // Note: Pixel ratio is currently always 1.0 + s += l2 + "readonly property vector3d iResolution: Qt.vector3d(width, height, 1.0)\n"; + } + if (m_shaderFeatures.enabled(ShaderFeatures::Mouse)) { // Do we need interactive effects? + s += l2 + "readonly property vector4d iMouse: Qt.vector4d(rootItem._effectMouseX, rootItem._effectMouseY,\n"; + s += l2 + " rootItem._effectMouseZ, rootItem._effectMouseW)\n"; + } + if (m_shaderFeatures.enabled(ShaderFeatures::BlurSources)) { + s += l2 + addProperty("iSourceBlur1", "blurSrc1", "Item", true); + s += l2 + addProperty("iSourceBlur2", "blurSrc2", "Item", true); + s += l2 + addProperty("iSourceBlur3", "blurSrc3", "Item", true); + s += l2 + addProperty("iSourceBlur4", "blurSrc4", "Item", true); + s += l2 + addProperty("iSourceBlur5", "blurSrc5", "Item", true); + } + // When used in preview component, we need property with value + // and when in exported component, property with binding to root value. + s += localFiles ? m_exportedEffectPropertiesString : m_previewEffectPropertiesString; + if (!customImagesString.isEmpty()) + s += '\n' + customImagesString; + + s += '\n'; + s += l2 + "vertexShader: '" + vertexShaderFilename + "'\n"; + s += l2 + "fragmentShader: '" + fragmentShaderFilename + "'\n"; + s += l2 + "anchors.fill: parent\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::GridMesh)) { + QString gridSize = QString("%1, %2").arg(m_shaderFeatures.gridMeshWidth()).arg(m_shaderFeatures.gridMeshHeight()); + s += l2 + "mesh: GridMesh {\n"; + s += l3 + QString("resolution: Qt.size(%1)\n").arg(gridSize); + s += l2 + "}\n"; + } + s += l1 + "}\n"; + return s; +} + void EffectMakerModel::updateQmlComponent() { // Clear possible QML runtime errors diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index af093de1814..395a9063c53 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -124,6 +124,7 @@ private: void updateCustomUniforms(); void bakeShaders(); + QString getQmlImagesString(bool localFiles); QString getQmlComponentString(bool localFiles); QList m_nodes; diff --git a/src/plugins/effectmakernew/shaderfeatures.cpp b/src/plugins/effectmakernew/shaderfeatures.cpp index 9ab7a282328..03aea9c1f15 100644 --- a/src/plugins/effectmakernew/shaderfeatures.cpp +++ b/src/plugins/effectmakernew/shaderfeatures.cpp @@ -77,5 +77,15 @@ void ShaderFeatures::checkLine(const QString &line, Features &features) features.setFlag(BlurSources, true); } +int ShaderFeatures::gridMeshHeight() const +{ + return m_gridMeshHeight; +} + +int ShaderFeatures::gridMeshWidth() const +{ + return m_gridMeshWidth; +} + } // namespace EffectMaker diff --git a/src/plugins/effectmakernew/shaderfeatures.h b/src/plugins/effectmakernew/shaderfeatures.h index c0d3b72b847..4e9f09eeae2 100644 --- a/src/plugins/effectmakernew/shaderfeatures.h +++ b/src/plugins/effectmakernew/shaderfeatures.h @@ -28,6 +28,10 @@ public: bool enabled(ShaderFeatures::Feature feature) const; + int gridMeshWidth() const; + + int gridMeshHeight() const; + private: void checkLine(const QString &line, ShaderFeatures::Features &features); ShaderFeatures::Features m_enabledFeatures; From 9074ccc0a44e184ac99cee1171e503dafdabb353 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Fri, 29 Sep 2023 19:11:58 +0200 Subject: [PATCH 119/130] QmlDesigner: Update connection editor tab document and screenshots This patch updates the Connection editor tab documentation from the Connections view. It also changes the old images to present the newly design connection editor in the document. Images were added in "webp" format. Fixes: QDS-10765 Change-Id: I1ead92ffff1f53c8e6560ed5b6a71501efa9d1bf Reviewed-by: Thomas Hartmann Reviewed-by: Johanna Vanhatapio --- ...nnections-ConditionalAction-Autometic.webp | Bin 0 -> 28716 bytes ...-connections-ConditionalAction-Manual.webp | Bin 0 -> 3358 bytes .../qmldesigner-connections-advanced.webp | Bin 0 -> 8672 bytes .../qmldesigner-connections-editor.webp | Bin 0 -> 10028 bytes .../qtquick-connection-editor-signals.qdoc | 155 +++++++++++------- 5 files changed, 96 insertions(+), 59 deletions(-) create mode 100644 doc/qtdesignstudio/images/qmldesigner-connections-ConditionalAction-Autometic.webp create mode 100644 doc/qtdesignstudio/images/qmldesigner-connections-ConditionalAction-Manual.webp create mode 100644 doc/qtdesignstudio/images/qmldesigner-connections-advanced.webp create mode 100644 doc/qtdesignstudio/images/qmldesigner-connections-editor.webp diff --git a/doc/qtdesignstudio/images/qmldesigner-connections-ConditionalAction-Autometic.webp b/doc/qtdesignstudio/images/qmldesigner-connections-ConditionalAction-Autometic.webp new file mode 100644 index 0000000000000000000000000000000000000000..41391ac8a7d5777786f88367992c86142ed004fc GIT binary patch literal 28716 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qmldesigner-connections-advanced.webp b/doc/qtdesignstudio/images/qmldesigner-connections-advanced.webp new file mode 100644 index 0000000000000000000000000000000000000000..1c59f96cc6a9b52a54bf6216e54a36ad4d67c9fb GIT binary patch literal 8672 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 F0|5T(7*YTL literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qmldesigner-connections-editor.webp b/doc/qtdesignstudio/images/qmldesigner-connections-editor.webp new file mode 100644 index 0000000000000000000000000000000000000000..a74d4c09dfc59bcbb8b25ec7e9c91d9364e561dc GIT binary patch literal 10028 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 d{F||Ci~gc}^ Date: Mon, 2 Oct 2023 18:44:25 +0200 Subject: [PATCH 120/130] QmlDesigner: Update Bindings editor tab document and screenshots This patch updates the Bindings editor tab documentation from the Connections view. It also changes the old images to present the newly design bindings editor in the document. Images were added in "webp" format. Fixes: QDS-10764 Change-Id: Id2b3a141b1aa40c8a141e1600bfa91fab9095347 Reviewed-by: Thomas Hartmann --- .../images/qmldesigner-bindings.webp | Bin 0 -> 7754 bytes .../qmldesigner-components-after-binding.webp | Bin 0 -> 306 bytes ...qmldesigner-components-before-binding.webp | Bin 0 -> 306 bytes .../qmldesigner-updated-bindings-editor.webp | Bin 0 -> 6204 bytes .../qtquick-connection-editor-bindings.qdoc | 53 +++++++++++------- 5 files changed, 32 insertions(+), 21 deletions(-) create mode 100644 doc/qtdesignstudio/images/qmldesigner-bindings.webp create mode 100644 doc/qtdesignstudio/images/qmldesigner-components-after-binding.webp create mode 100644 doc/qtdesignstudio/images/qmldesigner-components-before-binding.webp create mode 100644 doc/qtdesignstudio/images/qmldesigner-updated-bindings-editor.webp diff --git a/doc/qtdesignstudio/images/qmldesigner-bindings.webp b/doc/qtdesignstudio/images/qmldesigner-bindings.webp new file mode 100644 index 0000000000000000000000000000000000000000..492311bbb3736dd07b59d27dc66a6384efbc0010 GIT binary patch literal 7754 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qmldesigner-components-before-binding.webp b/doc/qtdesignstudio/images/qmldesigner-components-before-binding.webp new file mode 100644 index 0000000000000000000000000000000000000000..a53a1ed30b651603f859d19aa0add0ffcf01c28b GIT binary patch literal 306 zcmWIYbaT^UWMBw)bqWXzu<(&%WMI(0#%RYdeSr#tK+DoS>N~^pG&mVp9&-L~pLj>x zi;2PU$Nx@CmG7tiurvIrFMs~-&;H$;cc&dY^CIu=+TE$k&OCV+B5JIx)WIR3;+c2* zjhqWZ55t%L_tnzl4f9<7t3OctpP%>e*n9``Q}zGX{oWt*Z}R^q_CNjcQ17Fz)W`pe zwq3XW@oL%T^>)iMoJ(%bNs^kKoRz=ixRLzsXH#eWe6gYP{N(UynP&4_xA-)w%noL7 znLd?+!L2BA`n>c>xxG>+__9OPmYc4URKB*NapBs)qiYg+U#(%#SRK5LX+g`@%%l!O qH*f9Who=u}m#UmxShf1r>Z6*a6_Ind@~od7YT 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 k_!@opYQ)BrskgT6U!9|)`S300nmE9RL6T literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-editor-bindings.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-editor-bindings.qdoc index 613d8fd02f4..6d759f4de6b 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-editor-bindings.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-editor-bindings.qdoc @@ -25,30 +25,45 @@ You can create bindings between components in \uicontrol Bindings. - \image qmldesigner-bindings.png + \image qmldesigner-bindings.webp + + \section1 Creating Bindings Between Component Properties To bind a property of a component to the property of another component: \list 1 - \li Go to the \uicontrol Binding tab in the \l Connections view. + + \li Place two components in the \uicontrol {2D} view. + + \image qmldesigner-components-before-binding.webp + + \li Name the first component as \e {viewBox}. + \li Name the second component as \e {connectBox}. + \li Apply a thick \e black \uicontrol Border and a \e blue \uicontrol{Color} to the \e {viewBox} component. + \li Select the \e {connectBox} component. + \li Select \uicontrol Bindings from the \uicontrol Connections view. \li Select the \inlineimage icons/plus.png - (\uicontrol Add) button to add a binding for the currently selected - component. The component ID is displayed in the \uicontrol Item - column. - \li Double-click the value in the \uicontrol Property column to select - the property to bind to a source property. - \li Double-click the value in the \uicontrol {Source Item} column to - select the component whose property you want to use to determine the - behavior of the target component. - \li Double-click the value in the \uicontrol {Source Property} column - to select the property to bind the target property to. + (\uicontrol Add) button to add a binding to the currently selected + component. + + \image qmldesigner-updated-bindings-editor.webp + + \li From the pop-up \uicontrol {Bindings editor}, in the \uicontrol From section, + select \e {viewBox} as the parent component, then select its \uicontrol {border.color} + property. + \li In the \uicontrol To section you find the \e {connectBox} component already selected + as the target component. Select \uicontrol {color} from the \uicontrol {drop-down} + below to set its affected property. + \li You see the \uicontrol {border.color} of the \e {viewBox} component + instantly getting applied to the \uicontrol {color} of the \e {connectBox} + component. + + \image qmldesigner-components-after-binding.webp + \endlist - Right-click a binding and select \uicontrol {Open Binding Editor} in - the context menu to specify the binding as a JavaScript expression in - \uicontrol {Binding Editor}. For more information, see \l{Setting Bindings}. - - \image qmldesigner-binding-editor.png "Binding Editor" + All the \uicontrol Bindings connections have automated JavaScript expression in the + \uicontrol {Code view}. For more information, see \l{Setting Bindings}. For examples of creating property bindings, see: @@ -57,10 +72,6 @@ \li \l{Exporting Properties} \endlist - For more information, watch the following video: - - \youtube UfvA04CIXv0 - \include creator-logical-operators.qdocinc logical operators */ From ce6cc4aa4bf43ac5dee9294c7f2f55fdc165fd9b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 4 Oct 2023 11:59:55 +0200 Subject: [PATCH 121/130] QmlDesigenr: Fast fix because reviews takes so long Fixes: QDS-10822 Fixes: QDS-10823 Change-Id: Iaf2d44b30fb67e8a06a274aea066c33f9f2939d7 Reviewed-by: Marco Bubke --- src/plugins/qmldesigner/designercore/model/model.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index c0393d524c1..5bc081833ab 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -134,7 +134,8 @@ ModelPrivate::~ModelPrivate() void ModelPrivate::detachAllViews() { - projectStorage->removeRefreshCallback(&m_metaInfoRefreshCallback); + if constexpr (useProjectStorage()) + projectStorage->removeRefreshCallback(&m_metaInfoRefreshCallback); for (const QPointer &view : std::as_const(m_viewList)) detachView(view.data(), true); From 95d40394daa1059d22e89cd462cac0ea8474276f Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sat, 30 Sep 2023 15:08:51 +0200 Subject: [PATCH 122/130] Utils: Add C++ 20 converter function to_array That can be quite useful to create arrays: struct MyReallySpecialType { std::string_view name; std::string_view expression; }; std::string_view getExressionCArray(std::string_view name) { static constexpr MyReallySpecialType list[] = { {"Left", "true"}, {"Center", "execute(%1)"}, {"Right", "false"}}; auto found =std::find_if( std::begin(list), std::end(list), [&] (const auto &entry) { return entry.name == name; }); if (found != std::end(list)) return found->expression; return {}; } std::string_view getExressionStdArray(std::string_view name) { static constexpr auto list = Utils::to_array({ {"Left", "true"}, {"Center", "execute(%1)"}, {"Right", "false"}}); auto found = std::find_if( std::begin(list), std::end(list), [&] (const auto &entry) { return entry.name == name; }); if (found != std::end(list)) return found->expression; return {}; } Change-Id: Ifc737edb72d7a5f63b26203a5f22bfde19b5c2bc Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/utils/CMakeLists.txt | 1 + src/libs/utils/array.h | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/libs/utils/array.h diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index a0ec62ac244..146aeafbe5f 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -11,6 +11,7 @@ add_qtc_library(Utils ansiescapecodehandler.cpp ansiescapecodehandler.h appmainwindow.cpp appmainwindow.h archive.cpp archive.h + array.h aspects.cpp aspects.h async.cpp async.h basetreeview.cpp basetreeview.h diff --git a/src/libs/utils/array.h b/src/libs/utils/array.h new file mode 100644 index 00000000000..0d9a6047281 --- /dev/null +++ b/src/libs/utils/array.h @@ -0,0 +1,23 @@ +// 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 + +namespace Utils { + +namespace Internal { +template +constexpr std::array, size> to_array_implementation( + Type (&&array)[size], std::index_sequence) +{ + return {{std::move(array[index])...}}; +} +} // namespace Internal + +template +constexpr std::array, size> to_array(Type (&&array)[size]) +{ + return Internal::to_array_implementation(std::move(array), std::make_index_sequence{}); +} + +} // namespace Utils From ebe1c889b79c7e6abd93adc8fe2f9c155d82c9fa Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 7 Sep 2023 14:49:44 +0200 Subject: [PATCH 123/130] QmlDesigner: Integrate component generation It is only activated if the project storage is activated Task-number: QDS-10578 Change-Id: Id93673eba470aa37a249072b3ef9e0231499095a Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 3 + .../propertycomponentgenerator.cpp | 82 ++++++++++--- .../propertycomponentgenerator.h | 15 +++ .../propertyeditorcomponentgenerator.cpp | 14 +-- .../propertyeditorcomponentgenerator.h | 6 +- .../propertyeditor/propertyeditorview.cpp | 108 ++++++++++++++---- .../propertyeditor/propertyeditorview.h | 5 + .../designercore/include/abstractview.h | 2 +- .../qmldesigner/designercore/include/model.h | 1 + .../designercore/model/abstractview.cpp | 2 +- .../qmldesigner/designercore/model/model.cpp | 23 +++- .../qmldesigner/designercore/model/model_p.h | 5 +- .../projectstorage/projectstorage.h | 14 +-- .../projectstorage/projectstorageinterface.h | 4 +- .../projectstorage/sourcepathcache.h | 2 + tests/unit/tests/mocks/abstractviewmock.h | 2 +- tests/unit/tests/mocks/projectstoragemock.cpp | 70 ++++++++++-- tests/unit/tests/mocks/projectstoragemock.h | 19 ++- .../propertycomponentgenerator-test.cpp | 52 +++++++++ .../propertyeditorcomponentgenerator-test.cpp | 56 ++++----- .../unit/tests/unittests/model/model-test.cpp | 8 +- .../projectstorage/projectstorage-test.cpp | 4 +- 22 files changed, 387 insertions(+), 110 deletions(-) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 6df5a632472..80273e863b8 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -575,6 +575,9 @@ extend_qtc_plugin(QmlDesigner modelnodeoperations.cpp modelnodeoperations.h formatoperation.cpp formatoperation.h navigation2d.cpp navigation2d.h + propertyeditorcomponentgenerator.cpp propertyeditorcomponentgenerator.h + propertycomponentgenerator.cpp propertycomponentgenerator.h + propertycomponentgeneratorinterface.h qmldesignericonprovider.cpp qmldesignericonprovider.h qmleditormenu.cpp qmleditormenu.h selectioncontext.cpp selectioncontext.h diff --git a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp index 7e5487909a8..8cc84058d20 100644 --- a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp +++ b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp @@ -5,6 +5,7 @@ #include #include +#include #include @@ -205,10 +206,10 @@ std::optional createEntry(QmlJS::SimpleReader needsTypeArg}; } -PropertyComponentGenerator::Entries createEntries(QmlJS::SimpleReaderNode::Ptr templateConfiguration, - Model *model, - const QString &templatesPath) +std::tuple createEntries( + QmlJS::SimpleReaderNode::Ptr templateConfiguration, Model *model, const QString &templatesPath) { + bool hasInvalidTemplates = false; PropertyComponentGenerator::Entries entries; entries.reserve(32); @@ -216,12 +217,14 @@ PropertyComponentGenerator::Entries createEntries(QmlJS::SimpleReaderNode::Ptr t for (const QmlJS::SimpleReaderNode::Ptr &node : nodes) { if (auto entry = createEntry(node.get(), model, templatesPath)) entries.push_back(*entry); + else + hasInvalidTemplates = true; } - return entries; + return {entries, hasInvalidTemplates}; } -QStringList createImports(QmlJS::SimpleReaderNode::Ptr templateConfiguration) +QStringList createImports(QmlJS::SimpleReaderNode *templateConfiguration) { auto property = templateConfiguration->property("imports"); return Utils::transform(property.value.toList(), @@ -232,17 +235,11 @@ QStringList createImports(QmlJS::SimpleReaderNode::Ptr templateConfiguration) PropertyComponentGenerator::PropertyComponentGenerator(const QString &propertyEditorResourcesPath, Model *model) - : m_entries(createEntries(createTemplateConfiguration(propertyEditorResourcesPath), - model, - propertyEditorResourcesPath + "/PropertyTemplates/")) + : m_templateConfiguration{createTemplateConfiguration(propertyEditorResourcesPath)} + , m_propertyTemplatesPath{propertyEditorResourcesPath + "/PropertyTemplates/"} { - auto templateConfiguration = createTemplateConfiguration(propertyEditorResourcesPath); - - m_entries = createEntries(templateConfiguration, - model, - propertyEditorResourcesPath + "/PropertyTemplates/"); - - m_imports = createImports(templateConfiguration); + setModel(model); + m_imports = createImports(m_templateConfiguration.get()); } PropertyComponentGenerator::Property PropertyComponentGenerator::create(const PropertyMetaInfo &property) const @@ -257,6 +254,61 @@ PropertyComponentGenerator::Property PropertyComponentGenerator::create(const Pr return generateComplexComponent(property, propertyType); } +void PropertyComponentGenerator::setModel(Model *model) +{ + if (model && m_model && m_model->projectStorage() == model->projectStorage()) { + m_model = model; + return; + } + + if (model) { + setEntries(m_templateConfiguration, model, m_propertyTemplatesPath); + } else { + m_entries.clear(); + m_entryTypeIds.clear(); + } + m_model = model; +} + +namespace { + +bool insect(const TypeIds &first, const TypeIds &second) +{ + bool intersecting = false; + + std::set_intersection(first.begin(), + first.end(), + second.begin(), + second.end(), + Utils::make_iterator([&](const auto &) { intersecting = true; })); + + return intersecting; +} + +} // namespace + +void PropertyComponentGenerator::setEntries(QmlJS::SimpleReaderNode::Ptr templateConfiguration, + Model *model, + const QString &propertyTemplatesPath) +{ + auto [entries, hasInvalidTemplates] = createEntries(templateConfiguration, + model, + propertyTemplatesPath); + m_entries = std::move(entries); + m_hasInvalidTemplates = hasInvalidTemplates; + m_entryTypeIds = Utils::transform(m_entries, + [](const auto &entry) { return entry.type.id(); }); + std::sort(m_entryTypeIds.begin(), m_entryTypeIds.end()); +} + +void PropertyComponentGenerator::refreshMetaInfos(const TypeIds &deletedTypeIds) +{ + if (!insect(deletedTypeIds, m_entryTypeIds) && !m_hasInvalidTemplates) + return; + + setEntries(m_templateConfiguration, m_model, m_propertyTemplatesPath); +} + const PropertyComponentGenerator::Entry *PropertyComponentGenerator::findEntry(const NodeMetaInfo &type) const { auto found = std::find_if(m_entries.begin(), m_entries.end(), [&](const auto &entry) { diff --git a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.h b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.h index 67239c16a70..dfa5f30db2d 100644 --- a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.h +++ b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.h @@ -10,6 +10,8 @@ #include +#include + #include #include #include @@ -36,6 +38,10 @@ public: QStringList imports() const override { return m_imports; } + void setModel(Model *model); + + void refreshMetaInfos(const TypeIds &deletedTypeIds); + private: const Entry *findEntry(const NodeMetaInfo &type) const; QString generateSubComponentText(Utils::SmallStringView propertyBaseName, @@ -45,9 +51,18 @@ private: Property generateComplexComponent(const PropertyMetaInfo &property, const NodeMetaInfo &propertyType) const; + void setEntries(QmlJS::SimpleReaderNode::Ptr templateConfiguration, + Model *model, + const QString &propertyTemplatesPath); + private: Entries m_entries; + TypeIds m_entryTypeIds; QStringList m_imports; + QPointer m_model; + QmlJS::SimpleReaderNode::Ptr m_templateConfiguration; + QString m_propertyTemplatesPath; + bool m_hasInvalidTemplates = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.cpp b/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.cpp index 272130b8f33..9b066d5fec0 100644 --- a/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.cpp +++ b/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.cpp @@ -75,13 +75,11 @@ Utils::SmallStringView propertyName(const GeneratorProperty &property) property); } -PropertyMetaInfos getUnmangedProperties(const NodeMetaInfo &nodeInfo) +PropertyMetaInfos getUnmangedProperties(const NodeMetaInfos &prototypes) { PropertyMetaInfos properties; properties.reserve(128); - auto prototypes = nodeInfo.selfAndPrototypes(); - for (const auto &prototype : prototypes) { if (prototype.propertyEditorPathId()) break; @@ -117,12 +115,12 @@ GeneratorProperties createSortedGeneratorProperties( } QString createPropertySections(const PropertyComponentGeneratorType &propertyGenerator, - const NodeMetaInfo &nodeInfo) + const NodeMetaInfos &prototypeChain) { QString propertyComponents; propertyComponents.reserve(100000); - auto generatorProperties = createSortedGeneratorProperties(getUnmangedProperties(nodeInfo), + auto generatorProperties = createSortedGeneratorProperties(getUnmangedProperties(prototypeChain), propertyGenerator); const auto begin = generatorProperties.begin(); @@ -143,12 +141,12 @@ QString createPropertySections(const PropertyComponentGeneratorType &propertyGen } // namespace -PropertyEditorTemplateGenerator::PropertyEditorTemplateGenerator( +PropertyEditorComponentGenerator::PropertyEditorComponentGenerator( const PropertyComponentGeneratorType &propertyGenerator) : m_propertyGenerator{propertyGenerator} {} -QString PropertyEditorTemplateGenerator::create(const NodeMetaInfo &nodeInfo, bool isComponent) +QString PropertyEditorComponentGenerator::create(const NodeMetaInfos &prototypeChain, bool isComponent) { return QString{R"xy( %1 @@ -171,7 +169,7 @@ QString PropertyEditorTemplateGenerator::create(const NodeMetaInfo &nodeInfo, bo .arg(createImports(m_propertyGenerator.imports()), componentButton(isComponent), QObject::tr("Exposed Custom Properties"), - createPropertySections(m_propertyGenerator, nodeInfo)); + createPropertySections(m_propertyGenerator, prototypeChain)); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.h b/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.h index 37dd824d43f..56fa5ef78dc 100644 --- a/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.h +++ b/src/plugins/qmldesigner/components/componentcore/propertyeditorcomponentgenerator.h @@ -14,13 +14,13 @@ using PropertyComponentGeneratorType = PropertyComponentGeneratorInterface; #else using PropertyComponentGeneratorType = PropertyComponentGenerator; #endif -class PropertyEditorTemplateGenerator +class PropertyEditorComponentGenerator { public: - PropertyEditorTemplateGenerator(const PropertyComponentGeneratorType &propertyGenerator); + PropertyEditorComponentGenerator(const PropertyComponentGeneratorType &propertyGenerator); - QString create(const NodeMetaInfo &nodeInfo, bool isComponent); + [[nodiscard]] QString create(const NodeMetaInfos &prototypeChain, bool isComponent); private: const PropertyComponentGeneratorType &m_propertyGenerator; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index bf35e2512eb..1fba74f2178 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -20,6 +20,7 @@ #include #include +#include #include @@ -65,6 +66,8 @@ PropertyEditorView::PropertyEditorView(AsynchronousImageCache &imageCache, , m_timerId(0) , m_stackedWidget(new PropertyEditorWidget()) , m_qmlBackEndForCurrentType(nullptr) + , m_propertyComponentGenerator{QmlDesigner::PropertyEditorQmlBackend::propertyEditorResourcesPath(), + model()} , m_locked(false) , m_setupCompleted(false) , m_singleShotTimer(new QTimer(this)) @@ -373,6 +376,11 @@ void PropertyEditorView::currentTimelineChanged(const ModelNode &) m_qmlBackEndForCurrentType->contextObject()->setHasActiveTimeline(QmlTimeline::hasActiveTimeline(this)); } +void PropertyEditorView::refreshMetaInfos(const TypeIds &deletedTypeIds) +{ + m_propertyComponentGenerator.refreshMetaInfos(deletedTypeIds); +} + void PropertyEditorView::updateSize() { if (!m_qmlBackEndForCurrentType) @@ -439,8 +447,8 @@ void PropertyEditorView::resetView() namespace { -std::tuple diffType(const NodeMetaInfo &commonAncestor, - const NodeMetaInfo &specificsClassMetaInfo) +[[maybe_unused]] std::tuple diffType(const NodeMetaInfo &commonAncestor, + const NodeMetaInfo &specificsClassMetaInfo) { NodeMetaInfo diffClassMetaInfo; QUrl qmlSpecificsFile; @@ -463,9 +471,9 @@ std::tuple diffType(const NodeMetaInfo &commonAncestor, return {diffClassMetaInfo, qmlSpecificsFile}; } -QString getSpecificQmlData(const NodeMetaInfo &commonAncestor, - const ModelNode &selectedNode, - const NodeMetaInfo &diffClassMetaInfo) +[[maybe_unused]] QString getSpecificQmlData(const NodeMetaInfo &commonAncestor, + const ModelNode &selectedNode, + const NodeMetaInfo &diffClassMetaInfo) { if (commonAncestor.isValid() && diffClassMetaInfo != selectedNode.metaInfo()) return PropertyEditorQmlBackend::templateGeneration(commonAncestor, @@ -534,33 +542,90 @@ void setupWidget(PropertyEditorQmlBackend *currentQmlBackend, stackedWidget->setCurrentWidget(currentQmlBackend->widget()); currentQmlBackend->contextObject()->triggerSelectionChanged(); } + +[[maybe_unused]] auto findPaneAndSpecificsPath(const NodeMetaInfos &prototypes, + const SourcePathCacheInterface &pathCache) +{ + Utils::PathString panePath; + Utils::PathString specificsPath; + + for (const NodeMetaInfo &prototype : prototypes) { + auto sourceId = prototype.propertyEditorPathId(); + if (sourceId) { + auto path = pathCache.sourcePath(sourceId); + if (path.endsWith("Pane.qml")) { + panePath = path; + if (panePath.size() && specificsPath.size()) + return std::make_tuple(panePath, specificsPath); + } else if (path.endsWith("Specifics.qml")) { + specificsPath = path; + if (panePath.size() && specificsPath.size()) + return std::make_tuple(panePath, specificsPath); + } + } + } + + return std::make_tuple(panePath, specificsPath); +} } // namespace void PropertyEditorView::setupQmlBackend() { - const NodeMetaInfo commonAncestor = PropertyEditorQmlBackend::findCommonAncestor(m_selectedNode); + if constexpr (useProjectStorage()) { + auto selfAndPrototypes = m_selectedNode.metaInfo().selfAndPrototypes(); + auto specificQmlData = m_propertyEditorComponentGenerator.create(selfAndPrototypes, + m_selectedNode.isComponent()); + auto [panePath, specificsPath] = findPaneAndSpecificsPath(selfAndPrototypes, + model()->pathCache()); + PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash, + QUrl::fromLocalFile( + QString{panePath}), + m_imageCache, + m_stackedWidget, + this); - const auto [qmlFileUrl, specificsClassMetaInfo] = PropertyEditorQmlBackend::getQmlUrlForMetaInfo( - commonAncestor); + setupCurrentQmlBackend(currentQmlBackend, + m_selectedNode, + QUrl::fromLocalFile(QString{specificsPath}), + currentState(), + this, + specificQmlData); - auto [diffClassMetaInfo, qmlSpecificsFile] = diffType(commonAncestor, specificsClassMetaInfo); + setupWidget(currentQmlBackend, this, m_stackedWidget); - QString specificQmlData = getSpecificQmlData(commonAncestor, m_selectedNode, diffClassMetaInfo); + m_qmlBackEndForCurrentType = currentQmlBackend; - PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash, - qmlFileUrl, - m_imageCache, - m_stackedWidget, - this); + setupInsight(rootModelNode(), currentQmlBackend); + } else { + const NodeMetaInfo commonAncestor = PropertyEditorQmlBackend::findCommonAncestor( + m_selectedNode); - setupCurrentQmlBackend( - currentQmlBackend, m_selectedNode, qmlSpecificsFile, currentState(), this, specificQmlData); + const auto [qmlFileUrl, specificsClassMetaInfo] = PropertyEditorQmlBackend::getQmlUrlForMetaInfo( + commonAncestor); - setupWidget(currentQmlBackend, this, m_stackedWidget); + auto [diffClassMetaInfo, qmlSpecificsFile] = diffType(commonAncestor, specificsClassMetaInfo); - m_qmlBackEndForCurrentType = currentQmlBackend; + QString specificQmlData = getSpecificQmlData(commonAncestor, m_selectedNode, diffClassMetaInfo); - setupInsight(rootModelNode(), currentQmlBackend); + PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash, + qmlFileUrl, + m_imageCache, + m_stackedWidget, + this); + + setupCurrentQmlBackend(currentQmlBackend, + m_selectedNode, + qmlSpecificsFile, + currentState(), + this, + specificQmlData); + + setupWidget(currentQmlBackend, this, m_stackedWidget); + + m_qmlBackEndForCurrentType = currentQmlBackend; + + setupInsight(rootModelNode(), currentQmlBackend); + } } void PropertyEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value) @@ -646,6 +711,9 @@ void PropertyEditorView::modelAttached(Model *model) { AbstractView::modelAttached(model); + if constexpr (useProjectStorage()) + m_propertyComponentGenerator.setModel(model); + if (debug) qDebug() << Q_FUNC_INFO; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h index bb2f9dc360c..cc9b5228392 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h @@ -9,6 +9,7 @@ #include #include +#include QT_BEGIN_NAMESPACE class QEvent; @@ -81,6 +82,8 @@ public: void currentTimelineChanged(const ModelNode &node) override; + void refreshMetaInfos(const TypeIds &deletedTypeIds) override; + protected: void timerEvent(QTimerEvent *event) override; void setupPane(const TypeName &typeName); @@ -113,6 +116,8 @@ private: //variables QString m_qmlDir; QHash m_qmlBackendHash; PropertyEditorQmlBackend *m_qmlBackEndForCurrentType; + PropertyComponentGenerator m_propertyComponentGenerator; + PropertyEditorComponentGenerator m_propertyEditorComponentGenerator{m_propertyComponentGenerator}; bool m_locked; bool m_setupCompleted; QTimer *m_singleShotTimer; diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 7ca01c687eb..0a3350b693b 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -148,7 +148,7 @@ public: virtual void modelAttached(Model *model); virtual void modelAboutToBeDetached(Model *model); - virtual void refreshMetaInfos(); + virtual void refreshMetaInfos(const TypeIds &deletedTypeIds); virtual void nodeCreated(const ModelNode &createdNode); virtual void nodeAboutToBeRemoved(const ModelNode &removedNode); diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index ac8182c0760..cc3b4aadf9c 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -136,6 +136,7 @@ public: void setMetaInfo(const MetaInfo &metaInfo); NodeMetaInfo boolMetaInfo() const; + NodeMetaInfo doubleMetaInfo() const; NodeMetaInfo flowViewFlowActionAreaMetaInfo() const; NodeMetaInfo flowViewFlowDecisionMetaInfo() const; NodeMetaInfo flowViewFlowItemMetaInfo() const; diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 2fe422487c9..a982d8dc5e5 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -164,7 +164,7 @@ void AbstractView::modelAboutToBeDetached(Model *) removeModel(); } -void AbstractView::refreshMetaInfos() {} +void AbstractView::refreshMetaInfos(const TypeIds &) {} /*! \enum QmlDesigner::AbstractView::PropertyChangeFlag diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 5bc081833ab..7b5868d6cf5 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -83,7 +83,8 @@ ModelPrivate::ModelPrivate(Model *model, m_currentStateNode = m_rootInternalNode; m_currentTimelineNode = m_rootInternalNode; - projectStorage->addRefreshCallback(&m_metaInfoRefreshCallback); + if constexpr (useProjectStorage()) + projectStorage->addRefreshCallback(&m_metaInfoRefreshCallback); } ModelPrivate::ModelPrivate(Model *model, @@ -106,7 +107,8 @@ ModelPrivate::ModelPrivate(Model *model, m_currentStateNode = m_rootInternalNode; m_currentTimelineNode = m_rootInternalNode; - projectStorage->addRefreshCallback(&m_metaInfoRefreshCallback); + if constexpr (useProjectStorage()) + projectStorage->addRefreshCallback(&m_metaInfoRefreshCallback); } ModelPrivate::ModelPrivate(Model *model, @@ -129,7 +131,8 @@ ModelPrivate::ModelPrivate(Model *model, ModelPrivate::~ModelPrivate() { - projectStorage->removeRefreshCallback(&m_metaInfoRefreshCallback); + if constexpr (useProjectStorage()) + projectStorage->removeRefreshCallback(&m_metaInfoRefreshCallback); }; void ModelPrivate::detachAllViews() @@ -391,9 +394,9 @@ void ModelPrivate::setTypeId(InternalNode *node, Utils::SmallStringView typeName } } -void ModelPrivate::emitRefreshMetaInfos() +void ModelPrivate::emitRefreshMetaInfos(const TypeIds &deletedTypeIds) { - notifyNodeInstanceViewLast([&](AbstractView *view) { view->refreshMetaInfos(); }); + notifyNodeInstanceViewLast([&](AbstractView *view) { view->refreshMetaInfos(deletedTypeIds); }); } void ModelPrivate::handleResourceSet(const ModelResourceSet &resourceSet) @@ -2086,6 +2089,16 @@ NodeMetaInfo Model::boolMetaInfo() const } } +NodeMetaInfo Model::doubleMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo(); + } else { + return metaInfo("QML.double"); + } +} + template NodeMetaInfo Model::createNodeMetaInfo() const { diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 1df16d24bb5..d09e4054ca2 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -317,7 +317,7 @@ private: EnabledViewRange enabledViews() const; ImportedTypeNameId importedTypeNameId(Utils::SmallStringView typeName); void setTypeId(InternalNode *node, Utils::SmallStringView typeName); - void emitRefreshMetaInfos(); + void emitRefreshMetaInfos(const TypeIds &deletedTypeIds); public: NotNullPointer projectStorage = nullptr; @@ -326,7 +326,8 @@ public: private: Model *m_model = nullptr; MetaInfo m_metaInfo; - std::function m_metaInfoRefreshCallback{[&] { emitRefreshMetaInfos(); }}; + std::function m_metaInfoRefreshCallback{ + [&](const TypeIds &deletedTypeIds) { emitRefreshMetaInfos(deletedTypeIds); }}; Imports m_imports; Imports m_possibleImportList; Imports m_usedImportList; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 464d0f3b443..3f47eb3f018 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -53,6 +53,7 @@ public: void synchronize(Storage::Synchronization::SynchronizationPackage package) override { + TypeIds deletedTypeIds; Sqlite::withImmediateTransaction(database, [&] { AliasPropertyDeclarations insertedAliasPropertyDeclarations; AliasPropertyDeclarations updatedAliasPropertyDeclarations; @@ -61,7 +62,6 @@ public: PropertyDeclarations relinkablePropertyDeclarations; Prototypes relinkablePrototypes; Prototypes relinkableExtensions; - TypeIds deletedTypeIds; TypeIds updatedTypeIds; updatedTypeIds.reserve(package.types.size()); @@ -111,7 +111,7 @@ public: commonTypeCache_.resetTypeIds(); }); - callRefreshMetaInfoCallback(); + callRefreshMetaInfoCallback(deletedTypeIds); } void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override @@ -123,12 +123,12 @@ public: }); } - void addRefreshCallback(std::function *callback) override + void addRefreshCallback(std::function *callback) override { m_refreshCallbacks.push_back(callback); } - void removeRefreshCallback(std::function *callback) override + void removeRefreshCallback(std::function *callback) override { m_refreshCallbacks.erase( std::find(m_refreshCallbacks.begin(), m_refreshCallbacks.end(), callback)); @@ -616,10 +616,10 @@ private: return selectAllModulesStatement.template valuesWithTransaction(); } - void callRefreshMetaInfoCallback() + void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds) { for (auto *callback : m_refreshCallbacks) - (*callback)(); + (*callback)(deletedTypeIds); } class AliasPropertyDeclaration @@ -2787,7 +2787,7 @@ public: Initializer initializer; mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}}; Storage::Info::CommonTypeCache commonTypeCache_{*this}; - std::vector *> m_refreshCallbacks; + std::vector *> m_refreshCallbacks; ReadWriteStatement<1, 3> upsertTypeStatement{ "INSERT INTO types(sourceId, name, traits) VALUES(?1, ?2, ?3) ON CONFLICT DO " "UPDATE SET traits=excluded.traits WHERE traits IS NOT " diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index daf4225ffab..e6962602b38 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -27,8 +27,8 @@ public: virtual void synchronize(Storage::Synchronization::SynchronizationPackage package) = 0; virtual void synchronizeDocumentImports(const Storage::Imports imports, SourceId sourceId) = 0; - virtual void addRefreshCallback(std::function *callback) = 0; - virtual void removeRefreshCallback(std::function *callback) = 0; + virtual void addRefreshCallback(std::function *callback) = 0; + virtual void removeRefreshCallback(std::function *callback) = 0; virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0; virtual std::optional diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcache.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcache.h index 68327ee7cb8..424638a64d9 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcache.h @@ -3,6 +3,7 @@ #pragma once +#include "projectstorage.h" #include "projectstorageexceptions.h" #include "projectstorageids.h" #include "sourcepath.h" @@ -15,6 +16,7 @@ #include #include +#include namespace QmlDesigner { diff --git a/tests/unit/tests/mocks/abstractviewmock.h b/tests/unit/tests/mocks/abstractviewmock.h index f01c9f5f64b..d7eab46acce 100644 --- a/tests/unit/tests/mocks/abstractviewmock.h +++ b/tests/unit/tests/mocks/abstractviewmock.h @@ -56,5 +56,5 @@ public: AbstractView::PropertyChangeFlags propertyChange), (override)); MOCK_METHOD(void, nodeAboutToBeRemoved, (const QmlDesigner::ModelNode &removedNode), (override)); - MOCK_METHOD(void, refreshMetaInfos, (), (override)); + MOCK_METHOD(void, refreshMetaInfos, (const QmlDesigner::TypeIds &), (override)); }; diff --git a/tests/unit/tests/mocks/projectstoragemock.cpp b/tests/unit/tests/mocks/projectstoragemock.cpp index b2445cd82ac..ac829d21a3f 100644 --- a/tests/unit/tests/mocks/projectstoragemock.cpp +++ b/tests/unit/tests/mocks/projectstoragemock.cpp @@ -122,6 +122,16 @@ void ProjectStorageMock::addExportedTypeName(QmlDesigner::TypeId typeId, exportedTypeName[typeId].emplace_back(moduleId, typeName); } +void ProjectStorageMock::removeExportedTypeName(QmlDesigner::TypeId typeId, + QmlDesigner::ModuleId moduleId, + Utils::SmallStringView typeName) +{ + ON_CALL(*this, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(TypeId{})); + ON_CALL(*this, fetchTypeIdByModuleIdAndExportedName(Eq(moduleId), Eq(typeName))) + .WillByDefault(Return(TypeId{})); + exportedTypeName.erase(typeId); +} + PropertyDeclarationId ProjectStorageMock::createProperty(TypeId typeId, Utils::SmallString name, PropertyDeclarationTraits traits, @@ -149,6 +159,24 @@ PropertyDeclarationId ProjectStorageMock::createProperty(TypeId typeId, return propertyId; } +void ProjectStorageMock::removeProperty(QmlDesigner::TypeId typeId, Utils::SmallString name) +{ + auto propertyId = propertyDeclarationId(typeId, name); + + ON_CALL(*this, propertyDeclarationId(Eq(typeId), Eq(name))) + .WillByDefault(Return(PropertyDeclarationId{})); + ON_CALL(*this, propertyName(Eq(propertyId))) + .WillByDefault(Return(std::optional{})); + + ON_CALL(*this, propertyDeclaration(Eq(propertyId))) + .WillByDefault(Return(std::optional{})); + + ON_CALL(*this, propertyDeclarationIds(Eq(typeId))) + .WillByDefault(Return(QVarLengthArray{})); + ON_CALL(*this, localPropertyDeclarationIds(Eq(typeId))) + .WillByDefault(Return(QVarLengthArray{})); +} + QmlDesigner::PropertyDeclarationId ProjectStorageMock::createProperty( QmlDesigner::TypeId typeId, Utils::SmallString name, QmlDesigner::TypeId propertyTypeId) { @@ -187,6 +215,14 @@ void addBaseProperties(TypeId typeId, TypeIds baseTypeIds, ProjectStorageMock &s } } } + +void setType(TypeId typeId, ModuleId moduleId, Utils::SmallStringView typeName, ProjectStorageMock &storage) +{ + ON_CALL(storage, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId)); + ON_CALL(storage, fetchTypeIdByModuleIdAndExportedName(Eq(moduleId), Eq(typeName))) + .WillByDefault(Return(typeId)); +} + } // namespace TypeId ProjectStorageMock::createType(ModuleId moduleId, @@ -205,13 +241,12 @@ TypeId ProjectStorageMock::createType(ModuleId moduleId, static TypeId typeId; incrementBasicId(typeId); - ON_CALL(*this, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId)); - ON_CALL(*this, fetchTypeIdByModuleIdAndExportedName(Eq(moduleId), Eq(typeName))) - .WillByDefault(Return(typeId)); + setType(typeId, moduleId, typeName, *this); + + addBaseProperties(typeId, baseTypeIds, *this); + addExportedTypeName(typeId, moduleId, typeName); - ON_CALL(*this, exportedTypeNames(Eq(typeId))).WillByDefault([&](TypeId id) { - return exportedTypeName[id]; - }); + PropertyDeclarationId defaultPropertyDeclarationId; if (defaultPropertyName.size()) { if (!defaultPropertyTypeId) { @@ -225,8 +260,7 @@ TypeId ProjectStorageMock::createType(ModuleId moduleId, } ON_CALL(*this, type(Eq(typeId))) - .WillByDefault( - Return(Storage::Info::Type{defaultPropertyDeclarationId, sourceId, typeTraits})); + .WillByDefault(Return(Storage::Info::Type{defaultPropertyDeclarationId, sourceId, typeTraits})); ON_CALL(*this, isBasedOn(Eq(typeId), Eq(typeId))).WillByDefault(Return(true)); @@ -240,11 +274,20 @@ TypeId ProjectStorageMock::createType(ModuleId moduleId, ON_CALL(*this, prototypeAndSelfIds(Eq(typeId))).WillByDefault(Return(selfAndPrototypes)); ON_CALL(*this, prototypeIds(Eq(typeId))).WillByDefault(Return(baseTypeIds)); - addBaseProperties(typeId, baseTypeIds, *this); - return typeId; } +void ProjectStorageMock::removeType(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName) +{ + auto oldTypeId = typeId(moduleId, typeName); + + setType(TypeId{}, moduleId, typeName, *this); + + removeExportedTypeName(oldTypeId, moduleId, typeName); + + ON_CALL(*this, type(Eq(oldTypeId))).WillByDefault(Return(std::optional{})); +} + QmlDesigner::TypeId ProjectStorageMock::createType(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName, QmlDesigner::Storage::TypeTraits typeTraits, @@ -286,6 +329,13 @@ QmlDesigner::TypeId ProjectStorageMock::createValue(QmlDesigner::ModuleId module return createType(moduleId, typeName, Storage::TypeTraits::Value, baseTypeIds); } +ProjectStorageMock::ProjectStorageMock() +{ + ON_CALL(*this, exportedTypeNames(_)).WillByDefault([&](TypeId id) { + return exportedTypeName[id]; + }); +} + void ProjectStorageMock::setupQtQuick() { setupIsBasedOn(*this); diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index 5c189af519f..e55841f4786 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -16,6 +16,7 @@ class ProjectStorageMock : public QmlDesigner::ProjectStorageInterface { public: + ProjectStorageMock(); virtual ~ProjectStorageMock() = default; void setupQtQuick(); @@ -45,6 +46,10 @@ public: QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName); + void removeExportedTypeName(QmlDesigner::TypeId typeId, + QmlDesigner::ModuleId moduleId, + Utils::SmallStringView typeName); + QmlDesigner::TypeId createType(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName, Utils::SmallStringView defaultPropertyName, @@ -54,6 +59,8 @@ public: QmlDesigner::TypeIds baseTypeIds = {}, QmlDesigner::SourceId sourceId = QmlDesigner::SourceId{}); + void removeType(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName); + QmlDesigner::TypeId createType(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName, QmlDesigner::Storage::TypeTraits typeTraits, @@ -87,6 +94,8 @@ public: Utils::SmallString name, QmlDesigner::TypeId propertyTypeId); + void removeProperty(QmlDesigner::TypeId typeId, Utils::SmallString name); + void createSignal(QmlDesigner::TypeId typeId, Utils::SmallString name); void createFunction(QmlDesigner::TypeId typeId, Utils::SmallString name); void setPropertyEditorPathId(QmlDesigner::TypeId typeId, QmlDesigner::SourceId sourceId); @@ -100,8 +109,14 @@ public: (const QmlDesigner::Storage::Imports imports, QmlDesigner::SourceId sourceId), (override)); - MOCK_METHOD(void, addRefreshCallback, (std::function * callback), (override)); - MOCK_METHOD(void, removeRefreshCallback, (std::function * callback), (override)); + MOCK_METHOD(void, + addRefreshCallback, + (std::function * callback), + (override)); + MOCK_METHOD(void, + removeRefreshCallback, + (std::function * callback), + (override)); MOCK_METHOD(QmlDesigner::ModuleId, moduleId, (::Utils::SmallStringView), (const, override)); diff --git a/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp b/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp index 353efb91f64..10e4cc32525 100644 --- a/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp +++ b/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp @@ -319,4 +319,56 @@ TEST_F(PropertyComponentGenerator, get_imports) Eq("import StudioTheme 1.0 as StudioTheme"))); } +TEST_F(PropertyComponentGenerator, set_model_to_null_removes_creates_only_monostates) +{ + QString expected = getExpectedContent("real", "x", "x"); + auto xProperty = itemMetaInfo.property("x"); + + generator.setModel(nullptr); + + ASSERT_THAT(generator.create(xProperty), VariantWith(std::monostate{})); +} + +TEST_F(PropertyComponentGenerator, set_model_fromn_null_updates_internal_state) +{ + generator.setModel(nullptr); + QString expected = getExpectedContent("real", "x", "x"); + auto xProperty = itemMetaInfo.property("x"); + + generator.setModel(&model); + + ASSERT_THAT(generator.create(xProperty), IsBasicProperty(StrippedStringEq(expected))); +} + +TEST_F(PropertyComponentGenerator, after_refresh_meta_infos_type_was_deleted) +{ + auto xProperty = itemMetaInfo.property("x"); + auto doubleMetaInfo = model.doubleMetaInfo(); + projectStorageMock.removeExportedTypeName(doubleMetaInfo.id(), + projectStorageMock.createModule("QML"), + "real"); + + generator.refreshMetaInfos({doubleMetaInfo.id()}); + + ASSERT_THAT(generator.create(xProperty), VariantWith(std::monostate{})); +} + +TEST_F(PropertyComponentGenerator, after_refresh_meta_infos_type_was_added) +{ + QString expected = getExpectedContent("real", "x", "x"); + auto xProperty = itemMetaInfo.property("x"); + auto doubleMetaInfo = model.doubleMetaInfo(); + projectStorageMock.removeExportedTypeName(doubleMetaInfo.id(), + projectStorageMock.createModule("QML"), + "real"); + generator.refreshMetaInfos({doubleMetaInfo.id()}); + projectStorageMock.addExportedTypeName(doubleMetaInfo.id(), + projectStorageMock.createModule("QML"), + "real"); + + generator.refreshMetaInfos({}); + + ASSERT_THAT(generator.create(xProperty), IsBasicProperty(StrippedStringEq(expected))); +} + } // namespace diff --git a/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp b/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp index d02b2b12cbc..3b9a8bbfe25 100644 --- a/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp +++ b/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp @@ -14,7 +14,7 @@ using BasicProperty = QmlDesigner::PropertyComponentGenerator::BasicProperty; using ComplexProperty = QmlDesigner::PropertyComponentGenerator::ComplexProperty; using QmlDesigner::PropertyMetaInfo; -class PropertyEditorTemplateGenerator : public ::testing::Test +class PropertyEditorComponentGenerator : public ::testing::Test { protected: QmlDesigner::NodeMetaInfo createType(Utils::SmallStringView name, @@ -85,13 +85,13 @@ protected: QmlDesigner::SourceId sourceId = QmlDesigner::SourceId::create(10); NiceMock projectStorageMock{sourceId}; NiceMock propertyGeneratorMock; - QmlDesigner::PropertyEditorTemplateGenerator generator{propertyGeneratorMock}; + QmlDesigner::PropertyEditorComponentGenerator generator{propertyGeneratorMock}; QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick"); QmlDesigner::NodeMetaInfo fooTypeInfo = createType("Foo"); QmlDesigner::TypeId dummyTypeId = projectStorageMock.commonTypeCache().builtinTypeId(); }; -TEST_F(PropertyEditorTemplateGenerator, no_properties_and_no_imports) +TEST_F(PropertyEditorComponentGenerator, no_properties_and_no_imports) { QString expectedText{ R"xy( @@ -110,12 +110,12 @@ TEST_F(PropertyEditorTemplateGenerator, no_properties_and_no_imports) } })xy"}; - auto text = generator.create(fooTypeInfo, false); + auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false); ASSERT_THAT(text, StrippedStringEq(expectedText)); } -TEST_F(PropertyEditorTemplateGenerator, properties_without_component_are_not_shows) +TEST_F(PropertyEditorComponentGenerator, properties_without_component_are_not_shows) { QString expectedText{ R"xy( @@ -135,12 +135,12 @@ TEST_F(PropertyEditorTemplateGenerator, properties_without_component_are_not_sho })xy"}; createProperty(fooTypeInfo.id(), "x", {}, dummyTypeId); - auto text = generator.create(fooTypeInfo, false); + auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false); ASSERT_THAT(text, StrippedStringEq(expectedText)); } -TEST_F(PropertyEditorTemplateGenerator, show_component_button_for_a_component_node) +TEST_F(PropertyEditorComponentGenerator, show_component_button_for_a_component_node) { QString expectedText{ R"xy( @@ -160,12 +160,12 @@ TEST_F(PropertyEditorTemplateGenerator, show_component_button_for_a_component_no } })xy"}; - auto text = generator.create(fooTypeInfo, true); + auto text = generator.create(fooTypeInfo.selfAndPrototypes(), true); ASSERT_THAT(text, StrippedStringEq(expectedText)); } -TEST_F(PropertyEditorTemplateGenerator, imports) +TEST_F(PropertyEditorComponentGenerator, imports) { QString expectedText{ R"xy( @@ -187,12 +187,12 @@ TEST_F(PropertyEditorTemplateGenerator, imports) })xy"}; setImports({"import QtQtuick", "import Studio 2.1"}); - auto text = generator.create(fooTypeInfo, false); + auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false); ASSERT_THAT(text, StrippedStringEq(expectedText)); } -TEST_F(PropertyEditorTemplateGenerator, basic_property) +TEST_F(PropertyEditorComponentGenerator, basic_property) { QString expectedText{ R"xy( @@ -220,12 +220,12 @@ TEST_F(PropertyEditorTemplateGenerator, basic_property) })xy"}; createBasicProperty(fooTypeInfo.id(), "value", {}, dummyTypeId, "Double{}"); - auto text = generator.create(fooTypeInfo, false); + auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false); ASSERT_THAT(text, StrippedStringEq(expectedText)); } -TEST_F(PropertyEditorTemplateGenerator, basic_properties_with_base_type) +TEST_F(PropertyEditorComponentGenerator, basic_properties_with_base_type) { QString expectedText{ R"xy( @@ -256,12 +256,12 @@ TEST_F(PropertyEditorTemplateGenerator, basic_properties_with_base_type) auto superFooInfo = createType("SuperFoo", {fooTypeInfo.id()}); createBasicProperty(superFooInfo.id(), "value", {}, dummyTypeId, "SuperDouble{}"); - auto text = generator.create(superFooInfo, false); + auto text = generator.create(superFooInfo.selfAndPrototypes(), false); ASSERT_THAT(text, StrippedStringEq(expectedText)); } -TEST_F(PropertyEditorTemplateGenerator, +TEST_F(PropertyEditorComponentGenerator, only_handle_basic_properties_for_types_without_specifics_or_panes) { QString expectedText{ @@ -293,12 +293,12 @@ TEST_F(PropertyEditorTemplateGenerator, createBasicProperty(superFooInfo.id(), "value", {}, dummyTypeId, "SuperDouble{}"); projectStorageMock.setPropertyEditorPathId(fooTypeInfo.id(), sourceId); - auto text = generator.create(superFooInfo, false); + auto text = generator.create(superFooInfo.selfAndPrototypes(), false); ASSERT_THAT(text, StrippedStringEq(expectedText)); } -TEST_F(PropertyEditorTemplateGenerator, order_basic_property) +TEST_F(PropertyEditorComponentGenerator, order_basic_property) { QString expectedText{ R"xy( @@ -330,12 +330,12 @@ TEST_F(PropertyEditorTemplateGenerator, order_basic_property) createBasicProperty(fooTypeInfo.id(), "x", {}, dummyTypeId, "AnotherX{}"); createBasicProperty(fooTypeInfo.id(), "y", {}, dummyTypeId, "AndY{}"); - auto text = generator.create(fooTypeInfo, false); + auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false); ASSERT_THAT(text, StrippedStringEq(expectedText)); } -TEST_F(PropertyEditorTemplateGenerator, complex_property) +TEST_F(PropertyEditorComponentGenerator, complex_property) { QString expectedText{ R"xy( @@ -356,12 +356,12 @@ TEST_F(PropertyEditorTemplateGenerator, complex_property) })xy"}; createComplexProperty(fooTypeInfo.id(), "value", {}, dummyTypeId, "Complex{}"); - auto text = generator.create(fooTypeInfo, false); + auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false); ASSERT_THAT(text, StrippedStringEq(expectedText)); } -TEST_F(PropertyEditorTemplateGenerator, complex_properties_with_base_type) +TEST_F(PropertyEditorComponentGenerator, complex_properties_with_base_type) { QString expectedText{ R"xy( @@ -385,12 +385,12 @@ TEST_F(PropertyEditorTemplateGenerator, complex_properties_with_base_type) auto superFooInfo = createType("SuperFoo", {fooTypeInfo.id()}); createComplexProperty(superFooInfo.id(), "value", {}, dummyTypeId, "SuperComplex{}"); - auto text = generator.create(superFooInfo, false); + auto text = generator.create(superFooInfo.selfAndPrototypes(), false); ASSERT_THAT(text, StrippedStringEq(expectedText)); } -TEST_F(PropertyEditorTemplateGenerator, +TEST_F(PropertyEditorComponentGenerator, only_handle_complex_properties_for_types_without_specifics_or_panes) { QString expectedText{ @@ -415,12 +415,12 @@ TEST_F(PropertyEditorTemplateGenerator, createComplexProperty(superFooInfo.id(), "value", {}, dummyTypeId, "SuperComplex{}"); projectStorageMock.setPropertyEditorPathId(fooTypeInfo.id(), sourceId); - auto text = generator.create(superFooInfo, false); + auto text = generator.create(superFooInfo.selfAndPrototypes(), false); ASSERT_THAT(text, StrippedStringEq(expectedText)); } -TEST_F(PropertyEditorTemplateGenerator, ordered_complex_property) +TEST_F(PropertyEditorComponentGenerator, ordered_complex_property) { QString expectedText{ R"xy( @@ -445,12 +445,12 @@ TEST_F(PropertyEditorTemplateGenerator, ordered_complex_property) createComplexProperty(fooTypeInfo.id(), "font", {}, dummyTypeId, "ComplexFont{}"); createComplexProperty(fooTypeInfo.id(), "anchors", {}, dummyTypeId, "Anchors{}"); - auto text = generator.create(fooTypeInfo, false); + auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false); ASSERT_THAT(text, StrippedStringEq(expectedText)); } -TEST_F(PropertyEditorTemplateGenerator, basic_is_placed_before_complex_components) +TEST_F(PropertyEditorComponentGenerator, basic_is_placed_before_complex_components) { QString expectedText{ R"xy( @@ -480,7 +480,7 @@ TEST_F(PropertyEditorTemplateGenerator, basic_is_placed_before_complex_component createBasicProperty(fooTypeInfo.id(), "x", {}, dummyTypeId, "Double{}"); createComplexProperty(fooTypeInfo.id(), "font", {}, dummyTypeId, "Font{}"); - auto text = generator.create(fooTypeInfo, false); + auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false); ASSERT_THAT(text, StrippedStringEq(expectedText)); } diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index b705c15ffad..71f6462c7cc 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -928,14 +928,16 @@ TEST_F(Model, remove_refresh_callback_from_project_storage) TEST_F(Model, refresh_callback_is_calling_abstract_view) { - std::function *callback = nullptr; + const QmlDesigner::TypeIds typeIds = {QmlDesigner::TypeId::create(3), + QmlDesigner::TypeId::create(1)}; + std::function *callback = nullptr; ON_CALL(projectStorageMock, addRefreshCallback(_)).WillByDefault([&](auto *c) { callback = c; }); QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", -1, -1, nullptr, {}}; model.attachView(&viewMock); - EXPECT_CALL(viewMock, refreshMetaInfos()); + EXPECT_CALL(viewMock, refreshMetaInfos(typeIds)); - (*callback)(); + (*callback)(typeIds); } } // namespace diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 059c49e1397..14974f71232 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -7195,11 +7195,11 @@ TEST_F(ProjectStorage, synchronize_property_editor_with_non_existing_type_name) TEST_F(ProjectStorage, call_refresh_callback_after_synchronization) { auto package{createSimpleSynchronizationPackage()}; - MockFunction callbackMock; + MockFunction callbackMock; auto callback = callbackMock.AsStdFunction(); storage.addRefreshCallback(&callback); - EXPECT_CALL(callbackMock, Call()); + EXPECT_CALL(callbackMock, Call(_)); storage.synchronize(package); } From 975a282130a846f55ef204436b96c01ddf17eab0 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 14 Sep 2023 17:34:29 +0200 Subject: [PATCH 124/130] QmlDesigner: Ensure that indirect imports are deleted too If we import an indirect import can be added too. So if we remove that import we have to remove the related indirect imports too. Change-Id: I4cb09fdc8986c473db2a8194766778956bc7069a Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/CMakeLists.txt | 16 ++-- .../projectstorage/projectstorage.h | 96 ++++++++++--------- .../projectstorage/projectstorage-test.cpp | 13 +++ 3 files changed, 72 insertions(+), 53 deletions(-) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 80273e863b8..0d7c39784c9 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -11,6 +11,10 @@ endif() add_compile_options("$<$:-Wno-error=maybe-uninitialized>") add_compile_options("$<$:-Wno-error=maybe-uninitialized>") +env_with_default("QDS_USE_PROJECTSTORAGE" ENV_QDS_USE_PROJECTSTORAGE OFF) +option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE}) +add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") + add_qtc_library(QmlDesignerUtils STATIC DEPENDS Qt::Gui Utils Qt::QmlPrivate Core @@ -64,7 +68,10 @@ add_qtc_library(QmlDesignerCore STATIC QmlDesignerUtils TextEditor Sqlite - DEFINES QMLDESIGNERCORE_LIBRARY QMLDESIGNERUTILS_LIBRARY + DEFINES + QMLDESIGNERCORE_LIBRARY + QMLDESIGNERUTILS_LIBRARY + $<$:QDS_USE_PROJECTSTORAGE> INCLUDES ${CMAKE_CURRENT_LIST_DIR} PUBLIC_INCLUDES @@ -76,13 +83,6 @@ add_qtc_library(QmlDesignerCore STATIC rewritertransaction.h ) - -if(TARGET QmlDesignerCore) - env_with_default("QDS_USE_PROJECTSTORAGE" ENV_QDS_USE_PROJECTSTORAGE OFF) - option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE}) - add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") -endif() - extend_qtc_library(QmlDesignerCore CONDITION ENABLE_COMPILE_WARNING_AS_ERROR PROPERTIES COMPILE_WARNING_AS_ERROR ON diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 3f47eb3f018..cc4de52663e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -559,6 +559,12 @@ public: return selectPropertyEditorPathIdStatement.template valueWithTransaction(typeId); } + Storage::Imports fetchDocumentImports() const + { + return selectAllDocumentImportForSourceIdStatement + .template valuesWithTransaction(); + } + private: class ModuleStorageAdapter { @@ -1577,32 +1583,31 @@ private: PropertyCompare{}); } - void insertDocumentImport(const Storage::Import &import, - Storage::Synchronization::ImportKind importKind, - ModuleId sourceModuleId, - ModuleExportedImportId moduleExportedImportId) + ImportId insertDocumentImport(const Storage::Import &import, + Storage::Synchronization::ImportKind importKind, + ModuleId sourceModuleId, + ImportId parentImportId) { if (import.version.minor) { - insertDocumentImportWithVersionStatement.write(import.sourceId, - import.moduleId, - sourceModuleId, - importKind, - import.version.major.value, - import.version.minor.value, - moduleExportedImportId); + return insertDocumentImportWithVersionStatement + .template value(import.sourceId, + import.moduleId, + sourceModuleId, + importKind, + import.version.major.value, + import.version.minor.value, + parentImportId); } else if (import.version.major) { - insertDocumentImportWithMajorVersionStatement.write(import.sourceId, - import.moduleId, - sourceModuleId, - importKind, - import.version.major.value, - moduleExportedImportId); + return insertDocumentImportWithMajorVersionStatement + .template value(import.sourceId, + import.moduleId, + sourceModuleId, + importKind, + import.version.major.value, + parentImportId); } else { - insertDocumentImportWithoutVersionStatement.write(import.sourceId, - import.moduleId, - sourceModuleId, - importKind, - moduleExportedImportId); + return insertDocumentImportWithoutVersionStatement.template value( + import.sourceId, import.moduleId, sourceModuleId, importKind, parentImportId); } } @@ -1638,11 +1643,8 @@ private: }; auto insert = [&](const Storage::Import &import) { - insertDocumentImport(import, importKind, import.moduleId, ModuleExportedImportId{}); - auto callback = [&](ModuleId exportedModuleId, - int majorVersion, - int minorVersion, - ModuleExportedImportId moduleExportedImportId) { + auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{}); + auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) { Storage::Import additionImport{exportedModuleId, Storage::Version{majorVersion, minorVersion}, import.sourceId}; @@ -1651,10 +1653,7 @@ private: ? Storage::Synchronization::ImportKind::ModuleExportedImport : Storage::Synchronization::ImportKind::ModuleExportedModuleDependency; - insertDocumentImport(additionImport, - exportedImportKind, - import.moduleId, - moduleExportedImportId); + insertDocumentImport(additionImport, exportedImportKind, import.moduleId, importId); }; selectModuleExportedImportsForModuleIdStatement.readCallback(callback, @@ -1669,6 +1668,7 @@ private: auto remove = [&](const Storage::Synchronization::ImportView &view) { deleteDocumentImportStatement.write(view.importId); + deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId); }; Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove); @@ -2700,21 +2700,21 @@ private: Sqlite::StrictColumnType::Integer); auto &minorVersionColumn = table.addColumn("minorVersion", Sqlite::StrictColumnType::Integer); - auto &moduleExportedModuleIdColumn = table.addColumn("moduleExportedModuleId", - Sqlite::StrictColumnType::Integer); + auto &parentImportIdColumn = table.addColumn("parentImportId", + Sqlite::StrictColumnType::Integer); table.addUniqueIndex({sourceIdColumn, moduleIdColumn, kindColumn, sourceModuleIdColumn, - moduleExportedModuleIdColumn}, + parentImportIdColumn}, "majorVersion IS NULL AND minorVersion IS NULL"); table.addUniqueIndex({sourceIdColumn, moduleIdColumn, kindColumn, sourceModuleIdColumn, majorVersionColumn, - moduleExportedModuleIdColumn}, + parentImportIdColumn}, "majorVersion IS NOT NULL AND minorVersion IS NULL"); table.addUniqueIndex({sourceIdColumn, moduleIdColumn, @@ -2722,7 +2722,7 @@ private: sourceModuleIdColumn, majorVersionColumn, minorVersionColumn, - moduleExportedModuleIdColumn}, + parentImportIdColumn}, "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); table.initialize(database); @@ -3064,25 +3064,32 @@ public: "SELECT typeId FROM exportedTypeNames WHERE moduleId IN carray(?1, ?2, 'int32') AND " "name=?3", database}; + mutable ReadStatement<4> selectAllDocumentImportForSourceIdStatement{ + "SELECT moduleId, majorVersion, minorVersion, sourceId " + "FROM documentImports ", + database}; mutable ReadStatement<5, 2> selectDocumentImportForSourceIdStatement{ "SELECT importId, sourceId, moduleId, majorVersion, minorVersion " "FROM documentImports WHERE sourceId IN carray(?1) AND kind=?2 ORDER BY sourceId, " "moduleId, majorVersion, minorVersion", database}; - WriteStatement<5> insertDocumentImportWithoutVersionStatement{ + ReadWriteStatement<1, 5> insertDocumentImportWithoutVersionStatement{ "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, " - "moduleExportedModuleId) VALUES (?1, ?2, ?3, ?4, ?5)", + "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5) RETURNING importId", database}; - WriteStatement<6> insertDocumentImportWithMajorVersionStatement{ + ReadWriteStatement<1, 6> insertDocumentImportWithMajorVersionStatement{ "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, " - "moduleExportedModuleId) VALUES (?1, ?2, ?3, ?4, ?5, ?6)", + "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6) RETURNING importId", database}; - WriteStatement<7> insertDocumentImportWithVersionStatement{ + ReadWriteStatement<1, 7> insertDocumentImportWithVersionStatement{ "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, " - "minorVersion, moduleExportedModuleId) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)", + "minorVersion, parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7) RETURNING " + "importId", database}; WriteStatement<1> deleteDocumentImportStatement{"DELETE FROM documentImports WHERE importId=?1", database}; + WriteStatement<2> deleteDocumentImportsWithParentImportIdStatement{ + "DELETE FROM documentImports WHERE sourceId=?1 AND parentImportId=?2", database}; WriteStatement<1> deleteDocumentImportsWithSourceIdsStatement{ "DELETE FROM documentImports WHERE sourceId IN carray(?1)", database}; ReadStatement<1, 2> selectPropertyDeclarationIdPrototypeChainDownStatement{ @@ -3328,7 +3335,7 @@ public: database}; WriteStatement<1> deleteModuleExportedImportStatement{ "DELETE FROM moduleExportedImports WHERE moduleExportedImportId=?1", database}; - mutable ReadStatement<4, 3> selectModuleExportedImportsForModuleIdStatement{ + mutable ReadStatement<3, 3> selectModuleExportedImportsForModuleIdStatement{ "WITH RECURSIVE " " imports(moduleId, majorVersion, minorVersion, moduleExportedImportId) AS ( " " SELECT exportedModuleId, " @@ -3342,8 +3349,7 @@ public: " iif(mei.isAutoVersion=1, i.minorVersion, mei.minorVersion), " " mei.moduleExportedImportId " " FROM moduleExportedImports AS mei JOIN imports AS i USING(moduleId)) " - "SELECT DISTINCT moduleId, ifnull(majorVersion, -1), ifnull(minorVersion, -1), " - " moduleExportedImportId " + "SELECT DISTINCT moduleId, ifnull(majorVersion, -1), ifnull(minorVersion, -1) " "FROM imports", database}; mutable ReadStatement<1, 1> selectPropertyDeclarationIdsForTypeStatement{ diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 14974f71232..f6ea7a015a7 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -5332,6 +5332,19 @@ TEST_F(ProjectStorage, module_exported_import) UnorderedElementsAre(IsExportedType(myModuleModuleId, "MyItem")))))); } +TEST_F(ProjectStorage, module_exported_import_deletes_indirect_imports_too) +{ + auto package{createModuleExportedImportSynchronizationPackage()}; + storage.synchronize(package); + SynchronizationPackage packageWhichDeletesImports; + packageWhichDeletesImports.updatedSourceIds = package.updatedSourceIds; + storage.synchronize(packageWhichDeletesImports); + + auto imports = storage.fetchDocumentImports(); + + ASSERT_THAT(imports, IsEmpty()); +} + TEST_F(ProjectStorage, module_exported_import_with_different_versions) { auto package{createModuleExportedImportSynchronizationPackage()}; From f12e484056978b1f9a98fdfaa2f4ebfbaee4721a Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 18 Sep 2023 17:04:45 +0200 Subject: [PATCH 125/130] QmlDesigner: Remove typeName() usage from TextToModelMerger Task-number: QDS-10266 Change-Id: I81a8ff71f1e3b118bf472b266d4038ab85d6d617 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../designercore/include/nodemetainfo.h | 1 + .../designercore/metainfo/nodemetainfo.cpp | 21 +- .../designercore/model/texttomodelmerger.cpp | 182 +++++++++--------- .../designercore/model/texttomodelmerger.h | 15 +- .../projectstorage/projectstorageinfotypes.h | 3 +- .../projectstorage/qmltypesparser.cpp | 14 +- .../projectstorage/qmltypesparser-test.cpp | 83 ++++++++ 7 files changed, 223 insertions(+), 96 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index b91b650e50c..02a0c81ca4a 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -197,6 +197,7 @@ public: bool isVector3D() const; bool isVector4D() const; bool isView() const; + bool usesCustomParser() const; bool isEnumeration() const; QString importDirectoryPath() const; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index ef6ed17acc4..af660d538c3 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2204,6 +2204,23 @@ bool NodeMetaInfo::isView() const } } +bool NodeMetaInfo::usesCustomParser() const +{ + if constexpr (useProjectStorage()) { + return isValid() && bool(typeData().traits & Storage::TypeTraits::UsesCustomParser); + } else { + if (!isValid()) + return false; + + auto type = typeName(); + return type == "QtQuick.VisualItemModel" || type == "Qt.VisualItemModel" + || type == "QtQuick.VisualDataModel" || type == "Qt.VisualDataModel" + || type == "QtQuick.ListModel" || type == "Qt.ListModel" + || type == "QtQml.Models.ListModel" || type == "QtQuick.XmlListModel" + || type == "Qt.XmlListModel" || type == "QtQml.XmlListModel.XmlListModel"; + } +} + namespace { template @@ -2790,8 +2807,8 @@ bool NodeMetaInfo::isQmlComponent() const auto type = m_privateData->qualfiedTypeName(); return type == "Component" || type == "Qt.Component" || type == "QtQuick.Component" - || type == "QtQml.Component" || type == ".QQmlComponent" - || type == "QQmlComponent"; + || type == "QtQml.Component" || type == ".QQmlComponent" || type == "QQmlComponent" + || type == "QML.Component" || type == "QtQml.Base.Component"; } } diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index aed4a78d1d2..def00f395d3 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -296,23 +296,6 @@ bool isListElementType(const QmlDesigner::TypeName &type) return type == "ListElement" || type == "QtQuick.ListElement" || type == "Qt.ListElement"; } -bool isComponentType(const QmlDesigner::TypeName &type) -{ - return type == "Component" || type == "Qt.Component" || type == "QtQuick.Component" - || type == "QtQml.Component" || type == ".QQmlComponent" || type == "QQmlComponent" - || type == "QML.Component" || type == "QtQml.Base.Component"; -} - -bool isCustomParserType(const QmlDesigner::TypeName &type) -{ - return type == "QtQuick.VisualItemModel" || type == "Qt.VisualItemModel" - || type == "QtQuick.VisualDataModel" || type == "Qt.VisualDataModel" - || type == "QtQuick.ListModel" || type == "Qt.ListModel" - || type == "QtQml.Models.ListModel" || type == "QtQuick.XmlListModel" - || type == "Qt.XmlListModel"; -} - - bool isPropertyChangesType(const QmlDesigner::TypeName &type) { return type == "PropertyChanges" || type == "QtQuick.PropertyChanges" || type == "Qt.PropertyChanges"; @@ -324,9 +307,10 @@ bool isConnectionsType(const QmlDesigner::TypeName &type) || type == "QtQml.Connections" || type == "QtQml.Base.Connections"; } -bool propertyIsComponentType(const QmlDesigner::NodeAbstractProperty &property, const QmlDesigner::TypeName &type, QmlDesigner::Model *model) +bool propertyHasImplicitComponentType(const QmlDesigner::NodeAbstractProperty &property, + const QmlDesigner::NodeMetaInfo &type) { - if (model->metaInfo(type).isQmlComponent() && !isComponentType(type)) + if (type.isQmlComponent()) return false; //If the type is already a subclass of Component keep it return property.parentModelNode().isValid() @@ -427,6 +411,11 @@ bool equals(const QVariant &a, const QVariant &b) return false; } +bool usesCustomParserButIsNotPropertyChange(const QmlDesigner::NodeMetaInfo &nodeMetaInfo) +{ + return nodeMetaInfo.usesCustomParser() && !nodeMetaInfo.isQtQuickPropertyChanges(); +} + } // anonymous namespace namespace QmlDesigner { @@ -455,21 +444,20 @@ public: void enterScope(AST::Node *node) { m_scopeBuilder.push(node); } - void leaveScope() - { m_scopeBuilder.pop(); } + void leaveScope() { m_scopeBuilder.pop(); } - NodeMetaInfo lookup(AST::UiQualifiedId *astTypeNode) + std::tuple lookup(AST::UiQualifiedId *astTypeNode) { TypeName fullTypeName; for (AST::UiQualifiedId *iter = astTypeNode; iter; iter = iter->next) - if (!iter->name.isEmpty()) - fullTypeName += iter->name.toUtf8() + '.'; - - if (fullTypeName.endsWith('.')) - fullTypeName.chop(1); + if (!iter->name.isEmpty()) { + fullTypeName += iter->name.toUtf8(); + if (iter->next) + fullTypeName += '.'; + } NodeMetaInfo metaInfo = m_model->metaInfo(fullTypeName); - return metaInfo; + return {metaInfo, fullTypeName}; } bool lookupProperty(const QString &propertyPrefix, @@ -1241,21 +1229,15 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, m_rewriterView->positionStorage()->setNodeOffset(modelNode, astObjectType->identifierToken.offset); - NodeMetaInfo info = context->lookup(astObjectType); + auto [info, typeName] = context->lookup(astObjectType); if (!info.isValid()) { - qWarning() << "Skipping node with unknown type" << toString(astObjectType) << info.typeName(); + qWarning() << "Skipping node with unknown type" << toString(astObjectType); return; } int majorVersion = info.majorVersion(); int minorVersion = info.minorVersion(); - TypeName typeName = info.typeName(); - PropertyName defaultPropertyName = info.defaultPropertyName(); - - if (defaultPropertyName.isEmpty()) //fallback and use the meta system of the model - defaultPropertyName = modelNode.metaInfo().defaultPropertyName(); - if (modelNode.isRootNode() && !m_rewriterView->allowComponentRoot() && info.isQmlComponent()) { for (AST::UiObjectMemberList *iter = astInitializer->members; iter; iter = iter->next) { if (auto def = AST::cast(iter->member)) { @@ -1265,16 +1247,16 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, } } - bool isImplicitComponent = modelNode.hasParentProperty() && propertyIsComponentType(modelNode.parentProperty(), typeName, modelNode.model()); + bool isImplicitComponent = modelNode.hasParentProperty() + && propertyHasImplicitComponentType(modelNode.parentProperty(), info); - if (modelNode.type() - != typeName //If there is no valid parentProperty //the node has just been created. The type is correct then. + if (modelNode.metaInfo() + != info //If there is no valid parentProperty //the node has just been created. The type is correct then. || modelNode.majorVersion() != majorVersion || modelNode.minorVersion() != minorVersion || modelNode.behaviorPropertyName() != onTokenProperty) { const bool isRootNode = m_rewriterView->rootModelNode() == modelNode; - differenceHandler.typeDiffers(isRootNode, modelNode, typeName, - majorVersion, minorVersion, - astNode, context); + differenceHandler.typeDiffers( + isRootNode, modelNode, info, typeName, majorVersion, minorVersion, astNode, context); if (!modelNode.isValid()) return; @@ -1286,9 +1268,9 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, } } - if (isComponentType(typeName) || isImplicitComponent) + if (info.isQmlComponent() || isImplicitComponent) setupComponentDelayed(modelNode, differenceHandler.isAmender()); - else if (isCustomParserType(typeName)) + else if (info.usesCustomParser()) setupCustomParserNodeDelayed(modelNode, differenceHandler.isAmender()); else if (!modelNode.nodeSource().isEmpty() || modelNode.nodeSourceType() != ModelNode::NodeWithoutSource) clearImplicitComponentDelayed(modelNode, differenceHandler.isAmender()); @@ -1415,8 +1397,13 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, } } + PropertyName defaultPropertyName = info.defaultPropertyName(); + + if (defaultPropertyName.isEmpty()) //fallback and use the meta system of the model + defaultPropertyName = modelNode.metaInfo().defaultPropertyName(); + if (!defaultPropertyItems.isEmpty()) { - if (isComponentType(modelNode.type())) + if (modelNode.metaInfo().isQmlComponent()) setupComponentDelayed(modelNode, differenceHandler.isAmender()); if (defaultPropertyName.isEmpty()) { qWarning() << "No default property for node type" << modelNode.type() << ", ignoring child items."; @@ -1602,22 +1589,28 @@ void TextToModelMerger::syncNodeProperty(AbstractProperty &modelProperty, const TypeName &dynamicPropertyType, DifferenceHandler &differenceHandler) { - NodeMetaInfo info = context->lookup(binding->qualifiedTypeNameId); + auto [info, typeName] = context->lookup(binding->qualifiedTypeNameId); if (!info.isValid()) { qWarning() << "SNP" << "Skipping node with unknown type" << toString(binding->qualifiedTypeNameId); return; } - TypeName typeName = info.typeName(); - int majorVersion = info.majorVersion(); - int minorVersion = info.minorVersion(); + + int majorVersion = -1; + int minorVersion = -1; + if constexpr (!useProjectStorage()) { + typeName = info.typeName(); + majorVersion = info.majorVersion(); + minorVersion = info.minorVersion(); + } if (modelProperty.isNodeProperty() && dynamicPropertyType == modelProperty.dynamicTypeName()) { ModelNode nodePropertyNode = modelProperty.toNodeProperty().modelNode(); syncNode(nodePropertyNode, binding, context, differenceHandler); } else { differenceHandler.shouldBeNodeProperty(modelProperty, + info, typeName, majorVersion, minorVersion, @@ -1755,7 +1748,8 @@ void TextToModelMerger::syncNodeListProperty(NodeListProperty &modelListProperty } } -ModelNode TextToModelMerger::createModelNode(const TypeName &typeName, +ModelNode TextToModelMerger::createModelNode(const NodeMetaInfo &nodeMetaInfo, + const TypeName &typeName, int majorVersion, int minorVersion, bool isImplicitComponent, @@ -1775,13 +1769,17 @@ ModelNode TextToModelMerger::createModelNode(const TypeName &typeName, AST::UiQualifiedId *astObjectType = qualifiedTypeNameId(astNode); - if (isCustomParserType(typeName)) + bool useCustomParser = usesCustomParserButIsNotPropertyChange(nodeMetaInfo); + + if (useCustomParser) { nodeSource = textAt(context->doc(), - astObjectType->identifierToken, - astNode->lastSourceLocation()); + astObjectType->identifierToken, + astNode->lastSourceLocation()); + } + bool isQmlComponent = nodeMetaInfo.isQmlComponent(); - if (isComponentType(typeName) || isImplicitComponent) { + if (isQmlComponent || isImplicitComponent) { QString componentSource = extractComponentFromQml(textAt(context->doc(), astObjectType->identifierToken, astNode->lastSourceLocation())); @@ -1792,9 +1790,9 @@ ModelNode TextToModelMerger::createModelNode(const TypeName &typeName, ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource; - if (isComponentType(typeName) || isImplicitComponent) + if (isQmlComponent || isImplicitComponent) nodeSourceType = ModelNode::NodeWithComponentSource; - else if (isCustomParserType(typeName)) + else if (useCustomParser) nodeSourceType = ModelNode::NodeWithCustomParserSource; ModelNode newNode = m_rewriterView->createModelNode(typeName, @@ -1919,6 +1917,7 @@ void ModelValidator::shouldBeVariantProperty([[maybe_unused]] AbstractProperty & } void ModelValidator::shouldBeNodeProperty([[maybe_unused]] AbstractProperty &modelProperty, + const NodeMetaInfo &, const TypeName & /*typeName*/, int /*majorVersion*/, int /*minorVersion*/, @@ -1945,10 +1944,11 @@ ModelNode ModelValidator::listPropertyMissingModelNode(NodeListProperty &/*model } void ModelValidator::typeDiffers(bool /*isRootNode*/, - [[maybe_unused]] ModelNode &modelNode, - [[maybe_unused]] const TypeName &typeName, - [[maybe_unused]] int majorVersion, - [[maybe_unused]] int minorVersion, + ModelNode &modelNode, + const NodeMetaInfo &, + const TypeName &typeName, + int majorVersion, + int minorVersion, QmlJS::AST::UiObjectMember * /*astNode*/, ReadingContext * /*context*/) { @@ -2077,6 +2077,7 @@ void ModelAmender::shouldBeVariantProperty(AbstractProperty &modelProperty, cons } void ModelAmender::shouldBeNodeProperty(AbstractProperty &modelProperty, + const NodeMetaInfo &nodeMetaInfo, const TypeName &typeName, int majorVersion, int minorVersion, @@ -2087,15 +2088,17 @@ void ModelAmender::shouldBeNodeProperty(AbstractProperty &modelProperty, ModelNode theNode = modelProperty.parentModelNode(); NodeProperty newNodeProperty = theNode.nodeProperty(modelProperty.name()); - const bool propertyTakesComponent = propertyIsComponentType(newNodeProperty, typeName, theNode.model()); + const bool propertyTakesComponent = propertyHasImplicitComponentType(newNodeProperty, + nodeMetaInfo); - const ModelNode &newNode = m_merger->createModelNode(typeName, - majorVersion, - minorVersion, - propertyTakesComponent, - astNode, - context, - *this); + const ModelNode &newNode = m_merger->createModelNode(nodeMetaInfo, + typeName, + majorVersion, + minorVersion, + propertyTakesComponent, + astNode, + context, + *this); if (dynamicPropertyType.isEmpty()) newNodeProperty.setModelNode(newNode); @@ -2129,31 +2132,30 @@ ModelNode ModelAmender::listPropertyMissingModelNode(NodeListProperty &modelProp if (!astObjectType || !astInitializer) return ModelNode(); - NodeMetaInfo info = context->lookup(astObjectType); + auto [info, typeName] = context->lookup(astObjectType); if (!info.isValid()) { qWarning() << "Skipping node with unknown type" << toString(astObjectType); return {}; } - TypeName typeName = info.typeName(); - int majorVersion = info.majorVersion(); - int minorVersion = info.minorVersion(); - const bool propertyTakesComponent = propertyIsComponentType(modelProperty, typeName, m_merger->view()->model()); + int majorVersion = -1; + int minorVersion = -1; + if constexpr (!useProjectStorage()) { + typeName = info.typeName(); + majorVersion = info.majorVersion(); + minorVersion = info.minorVersion(); + } + const bool propertyTakesComponent = propertyHasImplicitComponentType(modelProperty, info); - const ModelNode &newNode = m_merger->createModelNode(typeName, - majorVersion, - minorVersion, - propertyTakesComponent, - arrayMember, - context, - *this); - + const ModelNode &newNode = m_merger->createModelNode( + info, typeName, majorVersion, minorVersion, propertyTakesComponent, arrayMember, context, *this); if (propertyTakesComponent) m_merger->setupComponentDelayed(newNode, true); - if (modelProperty.isDefaultProperty() || isComponentType(modelProperty.parentModelNode().type())) { //In the default property case we do some magic + if (modelProperty.isDefaultProperty() + || modelProperty.parentModelNode().metaInfo().isQmlComponent()) { //In the default property case we do some magic if (modelProperty.isNodeListProperty()) { modelProperty.reparentHere(newNode); } else { //The default property could a NodeProperty implicitly (delegate:) @@ -2168,13 +2170,16 @@ ModelNode ModelAmender::listPropertyMissingModelNode(NodeListProperty &modelProp void ModelAmender::typeDiffers(bool isRootNode, ModelNode &modelNode, + const NodeMetaInfo &nodeMetaInfo, const TypeName &typeName, int majorVersion, int minorVersion, AST::UiObjectMember *astNode, ReadingContext *context) { - const bool propertyTakesComponent = modelNode.hasParentProperty() && propertyIsComponentType(modelNode.parentProperty(), typeName, modelNode.model()); + const bool propertyTakesComponent = modelNode.hasParentProperty() + && propertyHasImplicitComponentType(modelNode.parentProperty(), + nodeMetaInfo); if (isRootNode) { modelNode.view()->changeRootNodeType(typeName, majorVersion, minorVersion); @@ -2182,13 +2187,14 @@ void ModelAmender::typeDiffers(bool isRootNode, NodeAbstractProperty parentProperty = modelNode.parentProperty(); int nodeIndex = -1; if (parentProperty.isNodeListProperty()) { - nodeIndex = parentProperty.toNodeListProperty().toModelNodeList().indexOf(modelNode); + nodeIndex = parentProperty.toNodeListProperty().indexOf(modelNode); Q_ASSERT(nodeIndex >= 0); } removeModelNode(modelNode); - const ModelNode &newNode = m_merger->createModelNode(typeName, + const ModelNode &newNode = m_merger->createModelNode(nodeMetaInfo, + typeName, majorVersion, minorVersion, propertyTakesComponent, @@ -2197,7 +2203,7 @@ void ModelAmender::typeDiffers(bool isRootNode, *this); parentProperty.reparentHere(newNode); if (parentProperty.isNodeListProperty()) { - int currentIndex = parentProperty.toNodeListProperty().toModelNodeList().indexOf(newNode); + int currentIndex = parentProperty.toNodeListProperty().indexOf(newNode); if (nodeIndex != currentIndex) parentProperty.toNodeListProperty().slide(currentIndex, nodeIndex); } @@ -2394,7 +2400,7 @@ void TextToModelMerger::setupCustomParserNode(const ModelNode &node) void TextToModelMerger::setupCustomParserNodeDelayed(const ModelNode &node, bool synchronous) { - Q_ASSERT(isCustomParserType(node.type())); + Q_ASSERT(usesCustomParserButIsNotPropertyChange(node.metaInfo())); if (synchronous) { setupCustomParserNode(node); @@ -2406,7 +2412,7 @@ void TextToModelMerger::setupCustomParserNodeDelayed(const ModelNode &node, bool void TextToModelMerger::clearImplicitComponentDelayed(const ModelNode &node, bool synchronous) { - Q_ASSERT(!isComponentType(node.type())); + Q_ASSERT(!usesCustomParserButIsNotPropertyChange(node.metaInfo())); if (synchronous) { clearImplicitComponent(node); diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h index 50b3a423678..f5119060405 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h @@ -95,7 +95,8 @@ public: const QList arrayMembers, ReadingContext *context, DifferenceHandler &differenceHandler); - ModelNode createModelNode(const TypeName &typeName, + ModelNode createModelNode(const QmlDesigner::NodeMetaInfo &nodeMetaInfo, + const TypeName &typeName, int majorVersion, int minorVersion, bool isImplicitComponent, @@ -183,23 +184,27 @@ public: virtual void variantValuesDiffer(VariantProperty &modelProperty, const QVariant &qmlVariantValue, const TypeName &dynamicTypeName) = 0; virtual void shouldBeVariantProperty(AbstractProperty &modelProperty, const QVariant &qmlVariantValue, const TypeName &dynamicTypeName) = 0; virtual void shouldBeNodeProperty(AbstractProperty &modelProperty, + const NodeMetaInfo &nodeMetaInfo, const TypeName &typeName, int majorVersion, int minorVersion, QmlJS::AST::UiObjectMember *astNode, const TypeName &dynamicPropertyType, - ReadingContext *context) = 0; + ReadingContext *context) + = 0; virtual void modelNodeAbsentFromQml(ModelNode &modelNode) = 0; virtual ModelNode listPropertyMissingModelNode(NodeListProperty &modelProperty, ReadingContext *context, QmlJS::AST::UiObjectMember *arrayMember) = 0; virtual void typeDiffers(bool isRootNode, ModelNode &modelNode, + const NodeMetaInfo &nodeMetaInfo, const TypeName &typeName, int majorVersion, int minorVersion, QmlJS::AST::UiObjectMember *astNode, - ReadingContext *context) = 0; + ReadingContext *context) + = 0; virtual void propertyAbsentFromQml(AbstractProperty &modelProperty) = 0; virtual void idsDiffer(ModelNode &modelNode, const QString &qmlId) = 0; virtual bool isAmender() const = 0; @@ -238,6 +243,7 @@ public: void variantValuesDiffer(VariantProperty &modelProperty, const QVariant &qmlVariantValue, const TypeName &dynamicTypeName) override; void shouldBeVariantProperty(AbstractProperty &modelProperty, const QVariant &qmlVariantValue, const TypeName &dynamicTypeName) override; void shouldBeNodeProperty(AbstractProperty &modelProperty, + const NodeMetaInfo &nodeMetaInfo, const TypeName &typeName, int majorVersion, int minorVersion, @@ -251,6 +257,7 @@ public: QmlJS::AST::UiObjectMember *arrayMember) override; void typeDiffers(bool isRootNode, ModelNode &modelNode, + const NodeMetaInfo &nodeMetaInfo, const TypeName &typeName, int majorVersion, int minorVersion, @@ -291,6 +298,7 @@ public: void variantValuesDiffer(VariantProperty &modelProperty, const QVariant &qmlVariantValue, const TypeName &dynamicType) override; void shouldBeVariantProperty(AbstractProperty &modelProperty, const QVariant &qmlVariantValue, const TypeName &dynamicTypeName) override; void shouldBeNodeProperty(AbstractProperty &modelProperty, + const NodeMetaInfo &nodeMetaInfo, const TypeName &typeName, int majorVersion, int minorVersion, @@ -304,6 +312,7 @@ public: QmlJS::AST::UiObjectMember *arrayMember) override; void typeDiffers(bool isRootNode, ModelNode &modelNode, + const NodeMetaInfo &nodeMetaInfo, const TypeName &typeName, int majorVersion, int minorVersion, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index 0d290c22f8c..bf73a3c724c 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -49,7 +49,8 @@ enum class TypeTraits : int { IsEnum = 1 << 8, IsFileComponent = 1 << 9, IsProjectComponent = 1 << 10, - IsInProjectModule = 1 << 11 + IsInProjectModule = 1 << 11, + UsesCustomParser = 1 << 12 }; constexpr TypeTraits operator|(TypeTraits first, TypeTraits second) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index 2c1adfba1ee..41f568e66d2 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -80,7 +80,7 @@ void addImports(Storage::Imports &imports, imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId); } -Storage::TypeTraits createTypeTraits(QQmlJSScope::AccessSemantics accessSematics) +Storage::TypeTraits createAccessTypeTraits(QQmlJSScope::AccessSemantics accessSematics) { switch (accessSematics) { case QQmlJSScope::AccessSemantics::Reference: @@ -96,6 +96,16 @@ Storage::TypeTraits createTypeTraits(QQmlJSScope::AccessSemantics accessSematics return Storage::TypeTraits::None; } +Storage::TypeTraits createTypeTraits(QQmlJSScope::AccessSemantics accessSematics, bool hasCustomParser) +{ + auto typeTrait = createAccessTypeTraits(accessSematics); + + if (hasCustomParser) + typeTrait = typeTrait | Storage::TypeTraits::UsesCustomParser; + + return typeTrait; +} + Storage::Version createVersion(QTypeRevision qmlVersion) { return Storage::Version{qmlVersion.majorVersion(), qmlVersion.minorVersion()}; @@ -413,7 +423,7 @@ void addType(Storage::Synchronization::Types &types, Utils::SmallStringView{typeName}, Storage::Synchronization::ImportedType{TypeNameString{component.baseTypeName()}}, Storage::Synchronization::ImportedType{TypeNameString{component.extensionTypeName()}}, - createTypeTraits(component.accessSemantics()), + createTypeTraits(component.accessSemantics(), component.hasCustomParser()), sourceId, createExports(exports, typeName, storage, cppModuleId), createProperties(component.ownProperties(), enumerationTypes, componentNameWithoutNamespace), diff --git a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp index 68138232b20..ba4b4865b86 100644 --- a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp @@ -53,6 +53,17 @@ MATCHER_P5(IsType, && type.traits == traits && type.sourceId == sourceId; } +MATCHER_P(HasFlag, flag, std::string(negation ? "hasn't " : "has ") + PrintToString(flag)) +{ + return bool(arg & flag); +} + +template +auto IsTypeTrait(const Matcher &matcher) +{ + return Field(&Synchronization::Type::traits, matcher); +} + MATCHER_P3(IsPropertyDeclaration, name, typeName, @@ -740,4 +751,76 @@ TEST_F(QmlTypesParser, alias_enumeration_is_referenced_by_qualified_name) QmlDesigner::Storage::PropertyDeclarationTraits::None))))); } +TEST_F(QmlTypesParser, access_type_is_reference) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject" + accessSemantics: "reference"}})"}; + + parser.parse(source, imports, types, projectData); + + ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraits::Reference))); +} + +TEST_F(QmlTypesParser, access_type_is_value) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject" + accessSemantics: "value"}})"}; + + parser.parse(source, imports, types, projectData); + + ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraits::Value))); +} + +TEST_F(QmlTypesParser, access_type_is_sequence) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject" + accessSemantics: "sequence"}})"}; + + parser.parse(source, imports, types, projectData); + + ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraits::Sequence))); +} + +TEST_F(QmlTypesParser, access_type_is_none) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject" + accessSemantics: "none"}})"}; + + parser.parse(source, imports, types, projectData); + + ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraits::None))); +} + +TEST_F(QmlTypesParser, uses_custom_parser) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject" + hasCustomParser: true }})"}; + + parser.parse(source, imports, types, projectData); + + ASSERT_THAT(types, ElementsAre(IsTypeTrait(HasFlag(Storage::TypeTraits::UsesCustomParser)))); +} + +TEST_F(QmlTypesParser, uses_no_custom_parser) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject" + hasCustomParser: false }})"}; + + parser.parse(source, imports, types, projectData); + + ASSERT_THAT(types, ElementsAre(IsTypeTrait(Not(HasFlag(Storage::TypeTraits::UsesCustomParser))))); +} + } // namespace From 3a73272e79a97c536876b3c76d7d37aceb457ad3 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 27 Sep 2023 17:03:40 +0200 Subject: [PATCH 126/130] QmlDesigner: Don't put asserts into code under tests Just write tests for it, so can handle that states gracefully. Otherwise the tests will crash for an assert. Change-Id: I275e73e3678a5cce7799146ee005244e6402181a Reviewed-by: Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Ali Kianian --- src/plugins/qmldesigner/designercore/model/modelutils.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp index 9ef98a39944..cb3f482289f 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -193,8 +193,6 @@ ModelNode lowestCommonAncestor(const ModelNode &node1, const int &depthOfNode1 = -1, const int &depthOfNode2 = -1) { - Q_ASSERT(node1.isValid() && node2.isValid()); - auto depthOfNode = [](const ModelNode &node) -> int { int depth = 0; ModelNode parentNode = node; From 7ae61da78dac3e2c239a2e9ec82be2c9fac9c38e Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Mon, 2 Oct 2023 20:20:46 +0200 Subject: [PATCH 127/130] QmlDesigner: Update Custom property editor tab document and screenshots This patch updates the custom property editor tab documentation from the Connections view. It also includes new images to present the newly design custom property in the document. Images were added in "webp" format. Fixes: QDS-10816 Change-Id: I37a1471477f0f58138819511aa18abe4d6b54e2f Reviewed-by: Thomas Hartmann --- ...-updated-local-custom-property-editor.webp | Bin 0 -> 6096 bytes .../add-updated-local-custom-property.webp | Bin 0 -> 8014 bytes .../qtquick-connection-editor-properties.qdoc | 26 ++++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 doc/qtdesignstudio/images/add-updated-local-custom-property-editor.webp create mode 100644 doc/qtdesignstudio/images/add-updated-local-custom-property.webp diff --git a/doc/qtdesignstudio/images/add-updated-local-custom-property-editor.webp b/doc/qtdesignstudio/images/add-updated-local-custom-property-editor.webp new file mode 100644 index 0000000000000000000000000000000000000000..17994a1edddb40ec019c0ebce20aebbeca69baba GIT binary patch literal 6096 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/add-updated-local-custom-property.webp b/doc/qtdesignstudio/images/add-updated-local-custom-property.webp new file mode 100644 index 0000000000000000000000000000000000000000..9c1f2ea9287da455d61e9bdd42254c6b48aac65a GIT binary patch literal 8014 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc index 8accd1df488..8ecfa8701b9 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc @@ -60,6 +60,32 @@ For more information, see \l{Setting Bindings}. + \section1 Adding a Custom Property to a Component from the Connections View + + You can add a custom property to a component from the \uicontrol {Connections} view. + Follow the process: + + \list 1 + \li Select the component you want to add a Custom property to in the + \uicontrol {2D} view or in the \uicontrol {Navigator} view. + \li Select \uicontrol {Properties} from the \uicontrol {Connections} view. + + \image add-updated-local-custom-property.webp + + \li Select the \inlineimage icons/plus.png + (\uicontrol Add) button to add a Custom property. + \li From the pop-up \uicontrol {Custom property editor}, select the \uicontrol {Type} + of the property you want to include. + + \image add-updated-local-custom-property-editor.webp + + \li Next, set the \uicontrol{Name} of the property. + \li Set a value to the Custom property in the \uicontrol {Value} field. + \endlist + + \note Select the \inlineimage icons/minus.png + (\uicontrol Remove) to delete a Custom Property. + \section1 Supported Property Types The following table describes the supported property types: From a4a8fd5b81f8fd7d06b132c68b6bcfcb78e9f8b8 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 28 Sep 2023 15:05:25 +0200 Subject: [PATCH 128/130] Utils: Extend transform for container insertion You can now return a container and it will be appended to the result container. That has the drawback of generating temporary container but if we get them already it makes the code much more readable than a raw loop. Change-Id: Ibcd38e85ef759c18cd8da0fac0185f0f6fc123e2 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Eike Ziller --- src/libs/utils/algorithm.h | 90 ++++++++++++++++++++++++-- tests/auto/algorithm/tst_algorithm.cpp | 18 ++++++ 2 files changed, 102 insertions(+), 6 deletions(-) diff --git a/src/libs/utils/algorithm.h b/src/libs/utils/algorithm.h index 2ea38377d8b..496a2f06298 100644 --- a/src/libs/utils/algorithm.h +++ b/src/libs/utils/algorithm.h @@ -597,13 +597,91 @@ public: MapInsertIterator operator++(int) { return *this; } }; -// inserter helper function, returns a std::back_inserter for most containers -// and is overloaded for QSet<> and other containers without push_back, returning custom inserters -template -inline std::back_insert_iterator -inserter(C &container) +// because Qt container are not implementing the standard interface we need +// this helper functions for generic code +template +void append(QList *container, QList &&input) { - return std::back_inserter(container); + container->append(std::move(input)); +} + +template +void append(QList *container, const QList &input) +{ + container->append(input); +} + +template +void append(Container *container, Container &&input) +{ + container->insert(container->end(), + std::make_move_iterator(input.begin()), + std::make_move_iterator(input.end())); +} + +template +void append(Container *container, const Container &input) +{ + container->insert(container->end(), input.begin(), input.end()); +} + +// BackInsertIterator behaves like std::back_insert_iterator except is adds the back insertion for +// container of the same type +template +class BackInsertIterator +{ +public: + using iterator_category = std::output_iterator_tag; + using value_type = void; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = void; + using container_type = Container; + + explicit constexpr BackInsertIterator(Container &container) + : m_container(std::addressof(container)) + {} + + constexpr BackInsertIterator &operator=(const typename Container::value_type &value) + { + m_container->push_back(value); + return *this; + } + + constexpr BackInsertIterator &operator=(typename Container::value_type &&value) + { + m_container->push_back(std::move(value)); + return *this; + } + + constexpr BackInsertIterator &operator=(const Container &container) + { + append(m_container, container); + return *this; + } + + constexpr BackInsertIterator &operator=(Container &&container) + { + append(m_container, container); + return *this; + } + + [[nodiscard]] constexpr BackInsertIterator &operator*() { return *this; } + + constexpr BackInsertIterator &operator++() { return *this; } + + constexpr BackInsertIterator operator++(int) { return *this; } + +private: + Container *m_container; +}; + +// inserter helper function, returns a BackInsertIterator for most containers +// and is overloaded for QSet<> and other containers without push_back, returning custom inserters +template +inline BackInsertIterator inserter(Container &container) +{ + return BackInsertIterator(container); } template diff --git a/tests/auto/algorithm/tst_algorithm.cpp b/tests/auto/algorithm/tst_algorithm.cpp index bca8a9dac0d..774ec637256 100644 --- a/tests/auto/algorithm/tst_algorithm.cpp +++ b/tests/auto/algorithm/tst_algorithm.cpp @@ -402,6 +402,24 @@ void tst_Algorithm::transform() const QHash expected({{1, 2}, {2, 3}, {3, 4}, {4, 5}}); QCOMPARE(trans, expected); } + { + // std::vector -> std::vector appending container + const std::vector v({1, 2, 3, 4}); + const auto trans = Utils::transform>(v, [](int i) -> std::vector { + return {i, i * 2}; + }); + const std::vector expected{1, 2, 2, 4, 3, 6, 4, 8}; + QCOMPARE(trans, expected); + } + { + // QList -> QList appending container + const QList v({1, 2, 3, 4}); + const auto trans = Utils::transform>(v, [](int i) -> QList { + return {i, i * 2}; + }); + const QList expected{1, 2, 2, 4, 3, 6, 4, 8}; + QCOMPARE(trans, expected); + } } void tst_Algorithm::sort() From a88caa00c7da8574b5f8d33ee9625aa4d944966f Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 4 Oct 2023 11:27:55 +0200 Subject: [PATCH 129/130] QmlDesigner: Ensure uniqueness of node meta info We test for equality by the pointer. To ensure it is uniqueness we now search for the qualified name. If we get a hit, we use that instance. We have to add the qualified key too into the cache to make it work. Change-Id: I7f09203aad5591c41798902a75fc971ece7e1c05 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../designercore/metainfo/nodemetainfo.cpp | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index af660d538c3..fcb66766797 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -719,7 +719,7 @@ PropertyName NodeMetaInfoPrivate::defaultPropertyName() const return PropertyName("data"); } -inline static TypeName stringIdentifier(const TypeName &type, int maj, int min) +static TypeName stringIdentifier(const TypeName &type, int maj, int min) { return type + QByteArray::number(maj) + '_' + QByteArray::number(min); } @@ -729,13 +729,30 @@ std::shared_ptr NodeMetaInfoPrivate::create(Model *model, int major, int minor) { + auto stringfiedType = stringIdentifier(type, major, minor); auto &cache = model->d->nodeMetaInfoCache(); - if (auto found = cache.find(stringIdentifier(type, major, minor)); found != cache.end()) + if (auto found = cache.find(stringfiedType); found != cache.end()) return *found; auto newData = std::make_shared(model, type, major, minor); - if (newData->isValid()) - cache.insert(stringIdentifier(type, major, minor), newData); + + if (!newData->isValid()) + return newData; + + auto stringfiedQualifiedType = stringIdentifier(newData->qualfiedTypeName(), + newData->majorVersion(), + newData->minorVersion()); + + if (auto found = cache.find(stringfiedQualifiedType); found != cache.end()) { + cache.insert(stringfiedType, *found); + return *found; + } + + if (stringfiedQualifiedType != stringfiedType) + cache.insert(stringfiedQualifiedType, newData); + + cache.insert(stringfiedType, newData); + return newData; } From 97ca8cc2700fb606fdcc1e3f089e904e6d308aba Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 4 Oct 2023 16:26:15 +0200 Subject: [PATCH 130/130] Core: add ICore::isQtDesignStudio() Change-Id: I3aed97b62abd05b283ac327be210af75f173383d Reviewed-by: Eike Ziller Reviewed-by: Thomas Hartmann --- src/plugins/coreplugin/icore.cpp | 6 ++++++ src/plugins/coreplugin/icore.h | 1 + src/plugins/mcusupport/mcusupportplugin.cpp | 9 +-------- .../componentcore/designeractionmanager.cpp | 4 +--- src/plugins/qmldesigner/designmodewidget.cpp | 2 +- src/plugins/qmldesigner/qmldesignerplugin.cpp | 8 ++++---- src/plugins/qmldesigner/settingspage.cpp | 5 ++--- src/plugins/qmlprojectmanager/qmlproject.cpp | 15 ++++----------- src/plugins/qmlprojectmanager/qmlproject.h | 1 - .../qmlprojectmanager/qmlprojectplugin.cpp | 4 ++-- .../qmlprojectrunconfiguration.cpp | 2 +- src/plugins/studiowelcome/studiowelcomeplugin.cpp | 2 +- 12 files changed, 24 insertions(+), 35 deletions(-) diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index fc6ec2bf925..3808b2413f7 100644 --- a/src/plugins/coreplugin/icore.cpp +++ b/src/plugins/coreplugin/icore.cpp @@ -341,6 +341,12 @@ bool ICore::showWarningWithOptions(const QString &title, const QString &text, return false; } + bool ICore::isQtDesignStudio() +{ + QtcSettings *settings = Core::ICore::settings(); + return settings->value("QML/Designer/StandAloneMode", false).toBool(); +} + /*! Returns the application's main settings object. diff --git a/src/plugins/coreplugin/icore.h b/src/plugins/coreplugin/icore.h index 1ecf6068536..5e28693904d 100644 --- a/src/plugins/coreplugin/icore.h +++ b/src/plugins/coreplugin/icore.h @@ -68,6 +68,7 @@ public: Utils::Id settingsId = {}, QWidget *parent = nullptr); + static bool isQtDesignStudio(); static Utils::QtcSettings *settings(QSettings::Scope scope = QSettings::UserScope); static SettingsDatabase *settingsDatabase(); static QPrinter *printer(); diff --git a/src/plugins/mcusupport/mcusupportplugin.cpp b/src/plugins/mcusupport/mcusupportplugin.cpp index c0548554627..21a993866b6 100644 --- a/src/plugins/mcusupport/mcusupportplugin.cpp +++ b/src/plugins/mcusupport/mcusupportplugin.cpp @@ -114,13 +114,6 @@ McuSupportPlugin::~McuSupportPlugin() dd = nullptr; } -static bool isQtDesignStudio() -{ - QSettings *settings = Core::ICore::settings(); - const QString qdsStandaloneEntry = "QML/Designer/StandAloneMode"; - return settings->value(qdsStandaloneEntry, false).toBool(); -} - void McuSupportPlugin::initialize() { setObjectName("McuSupportPlugin"); @@ -133,7 +126,7 @@ void McuSupportPlugin::initialize() // Temporary fix for CodeModel/Checker race condition // Remove after https://bugreports.qt.io/browse/QTCREATORBUG-29269 is closed - if (!isQtDesignStudio()) { + if (!Core::ICore::isQtDesignStudio()) { connect( QmlJS::ModelManagerInterface::instance(), &QmlJS::ModelManagerInterface::documentUpdated, diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 5239837ed49..4f800bf939f 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -1908,9 +1908,7 @@ void DesignerActionManager::createDefaultDesignerActions() &addMouseAreaFillCheck, &singleSelection)); - const bool standaloneMode = QmlProjectManager::QmlProject::isQtDesignStudio(); - - if (!standaloneMode) { + if (!Core::ICore::isQtDesignStudio()) { addDesignerAction(new ModelNodeContextMenuAction(goToImplementationCommandId, goToImplementationDisplayName, {}, diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index c42dd0673c6..fe9fafde5c4 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -97,7 +97,7 @@ DesignModeWidget::DesignModeWidget() , m_crumbleBar(new CrumbleBar(this)) { setAcceptDrops(true); - if (Utils::StyleHelper::isQDSTheme() || QmlProjectManager::QmlProject::isQtDesignStudio()) + if (Utils::StyleHelper::isQDSTheme() || Core::ICore::isQtDesignStudio()) qApp->setStyle(QmlDesignerBasePlugin::style()); } diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 839ad8b5683..f3b01c6bb78 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -268,7 +268,7 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e return false; d = new QmlDesignerPluginPrivate; d->timer.start(); - if (QmlProjectManager::QmlProject::isQtDesignStudio()) + if (Core::ICore::isQtDesignStudio()) GenerateResource::generateMenuEntry(this); const QString fontPath @@ -293,7 +293,7 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e Core::AsynchronousMessageBox::warning(composedTitle, description.toString()); }); - if (QmlProjectManager::QmlProject::isQtDesignStudio()) { + if (Core::ICore::isQtDesignStudio()) { d->toolBar = ToolBar::create(); d->statusBar = ToolBar::createStatusBar(); } @@ -328,7 +328,7 @@ void QmlDesignerPlugin::extensionsInitialized() ExtensionSystem::IPlugin::ShutdownFlag QmlDesignerPlugin::aboutToShutdown() { - if (QmlProjectManager::QmlProject::isQtDesignStudio()) + if (Core::ICore::isQtDesignStudio()) emitUsageStatistics("qdsShutdownCount"); return SynchronousShutdown; @@ -631,7 +631,7 @@ void QmlDesignerPlugin::enforceDelayedInitialize() d->viewManager.registerFormEditorTool(std::make_unique(d->externalDependencies)); d->viewManager.registerFormEditorTool(std::make_unique()); - if (QmlProjectManager::QmlProject::isQtDesignStudio()) { + if (Core::ICore::isQtDesignStudio()) { d->mainWidget.initialize(); emitUsageStatistics("StandaloneMode"); diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index 8c3222d72c3..473e1a341a5 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -473,16 +473,15 @@ void SettingsPageWidget::setSettings(const DesignerSettings &settings) m_askBeforeDeletingAssetCheckBox->setChecked(settings.value( DesignerSettingsKey::ASK_BEFORE_DELETING_ASSET).toBool()); - const bool standaloneMode = QmlProjectManager::QmlProject::isQtDesignStudio(); #ifdef QT_DEBUG const auto showDebugSettings = true; #else const auto showDebugSettings = settings.value(DesignerSettingsKey::SHOW_DEBUG_SETTINGS).toBool(); #endif - const bool showAdvancedFeatures = !standaloneMode || showDebugSettings; + const bool showAdvancedFeatures = !Core::ICore::isQtDesignStudio() || showDebugSettings; m_emulationGroupBox->setVisible(showAdvancedFeatures); m_debugGroupBox->setVisible(showAdvancedFeatures); - m_featureTimelineEditorCheckBox->setVisible(standaloneMode); + m_featureTimelineEditorCheckBox->setVisible(Core::ICore::isQtDesignStudio()); m_smoothRendering->setChecked(settings.value(DesignerSettingsKey::SMOOTH_RENDERING).toBool()); m_alwaysAutoFormatUICheckBox->setChecked( diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 619b590c112..ce7bd362877 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -51,7 +51,7 @@ QmlProject::QmlProject(const Utils::FilePath &fileName) setNeedsBuildConfigurations(false); setBuildSystemCreator([](Target *t) { return new QmlBuildSystem(t); }); - if (QmlProject::isQtDesignStudio()) { + if (Core::ICore::isQtDesignStudio()) { if (allowOnlySingleProject()) { EditorManager::closeAllDocuments(); ProjectManager::closeAllProjects(); @@ -111,7 +111,7 @@ Project::RestoreResult QmlProject::fromMap(const QVariantMap &map, QString *erro // FIXME: are there any other way? // What if it's not a Design Studio project? What should we do then? - if (QmlProject::isQtDesignStudio()) { + if (Core::ICore::isQtDesignStudio()) { int preferedVersion = preferedQtTarget(activeTarget()); setKitWithVersion(preferedVersion, kits); @@ -205,13 +205,6 @@ Tasks QmlProject::projectIssues(const Kit *k) const return result; } -bool QmlProject::isQtDesignStudio() -{ - QSettings *settings = Core::ICore::settings(); - const QString qdsStandaloneEntry = "QML/Designer/StandAloneMode"; - return settings->value(qdsStandaloneEntry, false).toBool(); -} - bool QmlProject::isQtDesignStudioStartedFromQtC() { return qEnvironmentVariableIsSet(Constants::enviromentLaunchedQDS); @@ -224,7 +217,7 @@ DeploymentKnowledge QmlProject::deploymentKnowledge() const bool QmlProject::isEditModePreferred() const { - return !isQtDesignStudio(); + return !Core::ICore::isQtDesignStudio(); } int QmlProject::preferedQtTarget(Target *target) @@ -238,7 +231,7 @@ int QmlProject::preferedQtTarget(Target *target) bool QmlProject::allowOnlySingleProject() { - QSettings *settings = Core::ICore::settings(); + auto settings = Core::ICore::settings(); auto key = "QML/Designer/AllowMultipleProjects"; return !settings->value(QString::fromUtf8(key), false).toBool(); } diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h index 3a6d5badf79..a2d3aeff11b 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.h +++ b/src/plugins/qmlprojectmanager/qmlproject.h @@ -17,7 +17,6 @@ class QMLPROJECTMANAGER_EXPORT QmlProject : public ProjectExplorer::Project public: explicit QmlProject(const Utils::FilePath &filename); - static bool isQtDesignStudio(); static bool isQtDesignStudioStartedFromQtC(); bool isEditModePreferred() const override; diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index 2876c3c366a..73f6aa438a6 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -264,7 +264,7 @@ void QmlProjectPlugin::initialize() Utils::FileIconProvider::registerIconOverlayForSuffix(":/qmlproject/images/qmlproject.png", "qmlproject"); - if (QmlProject::isQtDesignStudio()) { + if (Core::ICore::isQtDesignStudio()) { Core::ActionContainer *menu = Core::ActionManager::actionContainer( ProjectExplorer::Constants::M_FILECONTEXT); QAction *mainfileAction = new QAction(Tr::tr("Set as Main .qml File"), this); @@ -353,7 +353,7 @@ void QmlProjectPlugin::initialize() } GenerateCmake::generateMenuEntry(this); - if (QmlProject::isQtDesignStudio()) + if (Core::ICore::isQtDesignStudio()) GenerateCmake::CmakeProjectConverter::generateMenuEntry(this); } diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index 3678e789e13..44ae1bf40b6 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -245,7 +245,7 @@ FilePath QmlProjectRunConfiguration::qmlRuntimeFilePath() const void QmlProjectRunConfiguration::createQtVersionAspect() { - if (!QmlProject::isQtDesignStudio()) + if (!Core::ICore::isQtDesignStudio()) return; m_qtversionAspect = addAspect(); diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index 6b4168f9b64..5a7c4077a74 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -548,7 +548,7 @@ void StudioWelcomePlugin::extensionsInitialized() Core::ModeManager::activateMode(m_welcomeMode->id()); // Enable QDS new project dialog and QDS wizards - if (QmlProjectManager::QmlProject::isQtDesignStudio()) { + if (Core::ICore::isQtDesignStudio()) { ProjectExplorer::JsonWizardFactory::clearWizardPaths(); ProjectExplorer::JsonWizardFactory::addWizardPath( Core::ICore::resourcePath("qmldesigner/studio_templates"));