Connection Editor: ConnectionModelBackendDelegate refactoring

ConnectionModelBackendDelegate refactoring to extract logic responsible
for generating scripts to a reusable backend component.
ScriptEditorBackend can be used in the property editor to handle script
creation in scripting components (such as ScriptAction or
StateChangeScript) and as a base for ConnectionModelBackendDelegate.

Task-number: QDS-10449
Change-Id: Ie83f413f42bc1134ebb866aa6e7ea9d6b37da49b
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Rafal Stawarski
2025-02-20 16:37:24 +01:00
parent f8e71efc5a
commit de4b091dea
23 changed files with 1964 additions and 1844 deletions

View File

@@ -6,6 +6,7 @@ import QtQuick.Controls
import HelperWidgets as HelperWidgets import HelperWidgets as HelperWidgets
import StudioControls as StudioControls import StudioControls as StudioControls
import StudioTheme as StudioTheme import StudioTheme as StudioTheme
import ScriptEditorBackend
Column { Column {
id: root id: root
@@ -71,32 +72,32 @@ Column {
model: ListModel { model: ListModel {
ListElement { ListElement {
value: ConnectionModelStatementDelegate.CallFunction value: StatementDelegate.CallFunction
text: qsTr("Call Function") text: qsTr("Call Function")
enabled: true enabled: true
} }
ListElement { ListElement {
value: ConnectionModelStatementDelegate.Assign value: StatementDelegate.Assign
text: qsTr("Assign") text: qsTr("Assign")
enabled: true enabled: true
} }
ListElement { ListElement {
value: ConnectionModelStatementDelegate.ChangeState value: StatementDelegate.ChangeState
text: qsTr("Change State") text: qsTr("Change State")
enabled: true enabled: true
} }
ListElement { ListElement {
value: ConnectionModelStatementDelegate.SetProperty value: StatementDelegate.SetProperty
text: qsTr("Set Property") text: qsTr("Set Property")
enabled: true enabled: true
} }
ListElement { ListElement {
value: ConnectionModelStatementDelegate.PrintMessage value: StatementDelegate.PrintMessage
text: qsTr("Print Message") text: qsTr("Print Message")
enabled: true enabled: true
} }
ListElement { ListElement {
value: ConnectionModelStatementDelegate.Custom value: StatementDelegate.Custom
text: qsTr("Custom") text: qsTr("Custom")
enabled: false enabled: false
} }
@@ -106,7 +107,7 @@ Column {
StatementEditor { StatementEditor {
width: root.width width: root.width
actionType: action.currentValue ?? ConnectionModelStatementDelegate.Custom actionType: action.currentValue ?? StatementDelegate.Custom
horizontalSpacing: root.horizontalSpacing horizontalSpacing: root.horizontalSpacing
columnWidth: root.columnWidth columnWidth: root.columnWidth
statement: backend.okStatement statement: backend.okStatement
@@ -122,7 +123,7 @@ Column {
iconSize: StudioTheme.Values.baseFontSize iconSize: StudioTheme.Values.baseFontSize
iconFontFamily: StudioTheme.Constants.font.family iconFontFamily: StudioTheme.Constants.font.family
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && !backend.hasCondition visible: action.currentValue !== StatementDelegate.Custom && !backend.hasCondition
onClicked: backend.addCondition() onClicked: backend.addCondition()
} }
@@ -135,7 +136,7 @@ Column {
iconSize: StudioTheme.Values.baseFontSize iconSize: StudioTheme.Values.baseFontSize
iconFontFamily: StudioTheme.Constants.font.family iconFontFamily: StudioTheme.Constants.font.family
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && backend.hasCondition visible: action.currentValue !== StatementDelegate.Custom && backend.hasCondition
onClicked: backend.removeCondition() onClicked: backend.removeCondition()
} }
@@ -186,7 +187,7 @@ Column {
iconSize: StudioTheme.Values.baseFontSize iconSize: StudioTheme.Values.baseFontSize
iconFontFamily: StudioTheme.Constants.font.family iconFontFamily: StudioTheme.Constants.font.family
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: action.currentValue !== ConnectionModelStatementDelegate.Custom visible: action.currentValue !== StatementDelegate.Custom
&& backend.hasCondition && !backend.hasElse && backend.hasCondition && !backend.hasElse
onClicked: backend.addElse() onClicked: backend.addElse()
@@ -200,7 +201,7 @@ Column {
iconSize: StudioTheme.Values.baseFontSize iconSize: StudioTheme.Values.baseFontSize
iconFontFamily: StudioTheme.Constants.font.family iconFontFamily: StudioTheme.Constants.font.family
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: action.currentValue !== ConnectionModelStatementDelegate.Custom visible: action.currentValue !== StatementDelegate.Custom
&& backend.hasCondition && backend.hasElse && backend.hasCondition && backend.hasElse
onClicked: backend.removeElse() onClicked: backend.removeElse()
@@ -209,13 +210,13 @@ Column {
//Else Statement //Else Statement
StatementEditor { StatementEditor {
width: root.width width: root.width
actionType: action.currentValue ?? ConnectionModelStatementDelegate.Custom actionType: action.currentValue ?? StatementDelegate.Custom
horizontalSpacing: root.horizontalSpacing horizontalSpacing: root.horizontalSpacing
columnWidth: root.columnWidth columnWidth: root.columnWidth
statement: backend.koStatement statement: backend.koStatement
backend: root.backend backend: root.backend
spacing: root.verticalSpacing spacing: root.verticalSpacing
visible: action.currentValue !== ConnectionModelStatementDelegate.Custom visible: action.currentValue !== StatementDelegate.Custom
&& backend.hasCondition && backend.hasElse && backend.hasCondition && backend.hasElse
} }

View File

@@ -7,6 +7,7 @@ import HelperWidgets as HelperWidgets
import StudioControls as StudioControls import StudioControls as StudioControls
import StudioTheme as StudioTheme import StudioTheme as StudioTheme
import ConnectionsEditorEditorBackend import ConnectionsEditorEditorBackend
import ScriptEditorBackend
Column { Column {
id: root id: root
@@ -22,7 +23,7 @@ Column {
// Call Function // Call Function
Row { Row {
visible: root.actionType === ConnectionModelStatementDelegate.CallFunction visible: root.actionType === StatementDelegate.CallFunction
spacing: root.horizontalSpacing spacing: root.horizontalSpacing
PopupLabel { PopupLabel {
@@ -39,7 +40,7 @@ Column {
} }
Row { Row {
visible: root.actionType === ConnectionModelStatementDelegate.CallFunction visible: root.actionType === StatementDelegate.CallFunction
spacing: root.horizontalSpacing spacing: root.horizontalSpacing
StudioControls.TopLevelComboBox { StudioControls.TopLevelComboBox {
@@ -68,7 +69,7 @@ Column {
// Assign // Assign
Row { Row {
visible: root.actionType === ConnectionModelStatementDelegate.Assign visible: root.actionType === StatementDelegate.Assign
spacing: root.horizontalSpacing spacing: root.horizontalSpacing
PopupLabel { PopupLabel {
@@ -84,7 +85,7 @@ Column {
} }
Row { Row {
visible: root.actionType === ConnectionModelStatementDelegate.Assign visible: root.actionType === StatementDelegate.Assign
spacing: root.horizontalSpacing spacing: root.horizontalSpacing
StudioControls.TopLevelComboBox { StudioControls.TopLevelComboBox {
@@ -114,7 +115,7 @@ Column {
} }
Row { Row {
visible: root.actionType === ConnectionModelStatementDelegate.Assign visible: root.actionType === StatementDelegate.Assign
spacing: root.horizontalSpacing spacing: root.horizontalSpacing
StudioControls.TopLevelComboBox { StudioControls.TopLevelComboBox {
@@ -145,7 +146,7 @@ Column {
// Change State // Change State
Row { Row {
visible: root.actionType === ConnectionModelStatementDelegate.ChangeState visible: root.actionType === StatementDelegate.ChangeState
spacing: root.horizontalSpacing spacing: root.horizontalSpacing
PopupLabel { PopupLabel {
@@ -162,7 +163,7 @@ Column {
} }
Row { Row {
visible: root.actionType === ConnectionModelStatementDelegate.ChangeState visible: root.actionType === StatementDelegate.ChangeState
spacing: root.horizontalSpacing spacing: root.horizontalSpacing
StudioControls.TopLevelComboBox { StudioControls.TopLevelComboBox {
@@ -191,7 +192,7 @@ Column {
// Set Property // Set Property
Row { Row {
visible: root.actionType === ConnectionModelStatementDelegate.SetProperty visible: root.actionType === StatementDelegate.SetProperty
spacing: root.horizontalSpacing spacing: root.horizontalSpacing
PopupLabel { PopupLabel {
@@ -208,7 +209,7 @@ Column {
} }
Row { Row {
visible: root.actionType === ConnectionModelStatementDelegate.SetProperty visible: root.actionType === StatementDelegate.SetProperty
spacing: root.horizontalSpacing spacing: root.horizontalSpacing
StudioControls.TopLevelComboBox { StudioControls.TopLevelComboBox {
@@ -238,14 +239,14 @@ Column {
PopupLabel { PopupLabel {
width: root.columnWidth width: root.columnWidth
visible: root.actionType === ConnectionModelStatementDelegate.SetProperty visible: root.actionType === StatementDelegate.SetProperty
text: qsTr("Value") text: qsTr("Value")
tooltip: qsTr("Sets the value of the property of the component that is affected by the action of the <b>Target</b> component's <b>Signal</b>.") tooltip: qsTr("Sets the value of the property of the component that is affected by the action of the <b>Target</b> component's <b>Signal</b>.")
} }
StudioControls.TextField { StudioControls.TextField {
id: setPropertyArgument id: setPropertyArgument
visible: root.actionType === ConnectionModelStatementDelegate.SetProperty visible: root.actionType === StatementDelegate.SetProperty
width: root.width width: root.width
actionIndicatorVisible: false actionIndicatorVisible: false
translationIndicatorVisible: false translationIndicatorVisible: false
@@ -259,14 +260,14 @@ Column {
// Print Message // Print Message
PopupLabel { PopupLabel {
width: root.columnWidth width: root.columnWidth
visible: root.actionType === ConnectionModelStatementDelegate.PrintMessage visible: root.actionType === StatementDelegate.PrintMessage
text: qsTr("Message") text: qsTr("Message")
tooltip: qsTr("Sets a text that is printed when the <b>Signal</b> of the <b>Target</b> component initiates.") tooltip: qsTr("Sets a text that is printed when the <b>Signal</b> of the <b>Target</b> component initiates.")
} }
StudioControls.TextField { StudioControls.TextField {
id: messageString id: messageString
visible: root.actionType === ConnectionModelStatementDelegate.PrintMessage visible: root.actionType === StatementDelegate.PrintMessage
width: root.width width: root.width
actionIndicatorVisible: false actionIndicatorVisible: false
translationIndicatorVisible: false translationIndicatorVisible: false
@@ -278,7 +279,7 @@ Column {
// Custom // Custom
PopupLabel { PopupLabel {
visible: root.actionType === ConnectionModelStatementDelegate.Custom visible: root.actionType === StatementDelegate.Custom
text: qsTr("Custom Connections can only be edited with the binding editor") text: qsTr("Custom Connections can only be edited with the binding editor")
width: root.width width: root.width
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter

View File

@@ -571,6 +571,16 @@ extend_qtc_plugin(QmlDesigner
annotationeditor.qrc annotationeditor.qrc
) )
extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/scripteditor
SOURCES
scripteditorstatements.cpp scripteditorstatements.h
scripteditorevaluator.cpp scripteditorevaluator.h
scripteditorutils.cpp scripteditorutils.h
propertytreemodel.cpp propertytreemodel.h
scripteditorbackend.cpp scripteditorbackend.h
)
extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/connectioneditor SOURCES_PREFIX components/connectioneditor
SOURCES SOURCES
@@ -578,15 +588,12 @@ extend_qtc_plugin(QmlDesigner
bindingmodel.cpp bindingmodel.h bindingmodel.cpp bindingmodel.h
bindingmodelitem.cpp bindingmodelitem.h bindingmodelitem.cpp bindingmodelitem.h
connectioneditor.qrc connectioneditor.qrc
connectioneditorevaluator.cpp connectioneditorevaluator.h
connectioneditorstatements.cpp connectioneditorstatements.h
connectionmodel.cpp connectionmodel.h connectionmodel.cpp connectionmodel.h
connectionview.cpp connectionview.h connectionview.cpp connectionview.h
dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h
dynamicpropertiesitem.cpp dynamicpropertiesitem.h dynamicpropertiesitem.cpp dynamicpropertiesitem.h
connectioneditorutils.cpp connectioneditorutils.h
selectiondynamicpropertiesproxymodel.cpp selectiondynamicpropertiesproxymodel.h selectiondynamicpropertiesproxymodel.cpp selectiondynamicpropertiesproxymodel.h
propertytreemodel.cpp propertytreemodel.h connectioneditorlogging.cpp connectioneditorlogging.h
) )
extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner

View File

@@ -3,10 +3,11 @@
#include "bindingmodel.h" #include "bindingmodel.h"
#include "bindingmodelitem.h" #include "bindingmodelitem.h"
#include "connectioneditorutils.h" #include "connectioneditorlogging.h"
#include "connectionview.h" #include "connectionview.h"
#include "modelfwd.h" #include "modelfwd.h"
#include <scripteditorutils.h>
#include <bindingproperty.h> #include <bindingproperty.h>
#include <nodemetainfo.h> #include <nodemetainfo.h>
#include <nodeproperty.h> #include <nodeproperty.h>

View File

@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "bindingmodelitem.h" #include "bindingmodelitem.h"
#include "connectioneditorutils.h" #include <scripteditorutils.h>
#include <modelnode.h> #include <modelnode.h>
#include <qmldesignertr.h> #include <qmldesignertr.h>

View File

@@ -0,0 +1,10 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "connectioneditorlogging.h"
namespace QmlDesigner {
Q_LOGGING_CATEGORY(ConnectionEditorLog, "qtc.qtquickdesigner.connectioneditor", QtWarningMsg)
} // namespace QmlDesigner

View File

@@ -0,0 +1,12 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <QLoggingCategory>
namespace QmlDesigner {
Q_DECLARE_LOGGING_CATEGORY(ConnectionEditorLog)
} // namespace QmlDesigner

View File

@@ -3,15 +3,11 @@
#pragma once #pragma once
#include <connectioneditorstatements.h> #include <scripteditorbackend.h>
#include <propertytreemodel.h>
#include <studioquickwidget.h> #include <studioquickwidget.h>
#include <QAbstractListModel>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <memory>
namespace QmlDesigner { namespace QmlDesigner {
class AbstractProperty; class AbstractProperty;
@@ -23,247 +19,35 @@ class VariantProperty;
class ConnectionView; class ConnectionView;
class ConnectionModel; class ConnectionModel;
class ConditionListModel : public QAbstractListModel class ConnectionModelBackendDelegate : public ScriptEditorBackend
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool valid READ valid NOTIFY validChanged)
Q_PROPERTY(bool empty READ empty NOTIFY emptyChanged)
Q_PROPERTY(QString error READ error NOTIFY errorChanged)
Q_PROPERTY(int errorIndex READ errorIndex NOTIFY errorIndexChanged)
public:
enum ConditionType { Intermediate, Invalid, Operator, Literal, Variable, Shadow };
Q_ENUM(ConditionType)
struct ConditionToken
{
ConditionType type;
QString value;
};
ConditionListModel(ConnectionModel *model);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void setup();
void setCondition(ConnectionEditorStatements::MatchedCondition &condition);
ConnectionEditorStatements::MatchedCondition &condition();
static ConditionToken tokenFromConditionToken(const ConnectionEditorStatements::ConditionToken &token);
static ConditionToken tokenFromComparativeStatement(
const ConnectionEditorStatements::ComparativeStatement &token);
Q_INVOKABLE void insertToken(int index, const QString &value);
Q_INVOKABLE void updateToken(int index, const QString &value);
Q_INVOKABLE void appendToken(const QString &value);
Q_INVOKABLE void removeToken(int index);
Q_INVOKABLE void insertIntermediateToken(int index, const QString &value);
Q_INVOKABLE void insertShadowToken(int index, const QString &value);
Q_INVOKABLE void setShadowToken(int index, const QString &value);
bool valid() const;
bool empty() const;
//for debugging
Q_INVOKABLE void command(const QString &string);
void setInvalid(const QString &errorMessage, int index = -1);
void setValid();
QString error() const;
int errorIndex() const;
Q_INVOKABLE bool operatorAllowed(int cursorPosition);
signals:
void validChanged();
void emptyChanged();
void conditionChanged();
void errorChanged();
void errorIndexChanged();
private:
void internalSetup();
ConditionToken valueToToken(const QString &value);
void resetModel();
int checkOrder() const;
void validateAndRebuildTokens();
void rebuildTokens();
ConnectionEditorStatements::ConditionToken toOperatorStatement(const ConditionToken &token);
ConnectionEditorStatements::ComparativeStatement toStatement(const ConditionToken &token);
ConnectionModel *m_connectionModel = nullptr;
ConnectionEditorStatements::MatchedCondition &m_condition;
QList<ConditionToken> m_tokens;
bool m_valid = false;
QString m_errorMessage;
int m_errorIndex = -1;
};
class ConnectionModelStatementDelegate : public QObject
{
Q_OBJECT
public:
explicit ConnectionModelStatementDelegate(ConnectionModel *model);
enum ActionType { CallFunction, Assign, ChangeState, SetProperty, PrintMessage, Custom };
Q_ENUM(ActionType)
Q_PROPERTY(ActionType actionType READ actionType NOTIFY actionTypeChanged)
Q_PROPERTY(PropertyTreeModelDelegate *function READ function CONSTANT)
Q_PROPERTY(PropertyTreeModelDelegate *lhs READ lhs CONSTANT)
Q_PROPERTY(PropertyTreeModelDelegate *rhsAssignment READ rhsAssignment CONSTANT)
Q_PROPERTY(StudioQmlTextBackend *stringArgument READ stringArgument CONSTANT)
Q_PROPERTY(StudioQmlComboBoxBackend *states READ states CONSTANT)
Q_PROPERTY(StudioQmlComboBoxBackend *stateTargets READ stateTargets CONSTANT)
void setActionType(ActionType type);
void setup();
void setStatement(ConnectionEditorStatements::MatchedStatement &statement);
ConnectionEditorStatements::MatchedStatement &statement();
signals:
void actionTypeChanged();
void statementChanged();
private:
ActionType actionType() const;
PropertyTreeModelDelegate *signal();
PropertyTreeModelDelegate *function();
PropertyTreeModelDelegate *lhs();
PropertyTreeModelDelegate *rhsAssignment();
StudioQmlTextBackend *stringArgument();
StudioQmlComboBoxBackend *stateTargets();
StudioQmlComboBoxBackend *states();
void handleFunctionChanged();
void handleLhsChanged();
void handleRhsAssignmentChanged();
void handleStringArgumentChanged();
void handleStateChanged();
void handleStateTargetsChanged();
void setupAssignment();
void setupSetProperty();
void setupCallFunction();
void setupChangeState();
void setupStates();
void setupPrintMessage();
void setupPropertyType();
QString baseStateName() const;
ActionType m_actionType;
PropertyTreeModelDelegate m_functionDelegate;
PropertyTreeModelDelegate m_lhsDelegate;
PropertyTreeModelDelegate m_rhsAssignmentDelegate;
ConnectionEditorStatements::MatchedStatement &m_statement;
ConnectionModel *m_model = nullptr;
StudioQmlTextBackend m_stringArgument;
StudioQmlComboBoxBackend m_stateTargets;
StudioQmlComboBoxBackend m_states;
};
class ConnectionModelBackendDelegate : public QObject
{
Q_OBJECT
Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged) Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged)
Q_PROPERTY(ActionType actionType READ actionType NOTIFY actionTypeChanged)
Q_PROPERTY(PropertyTreeModelDelegate *signal READ signal CONSTANT) Q_PROPERTY(PropertyTreeModelDelegate *signal READ signal CONSTANT)
Q_PROPERTY(ConnectionModelStatementDelegate *okStatement READ okStatement CONSTANT)
Q_PROPERTY(ConnectionModelStatementDelegate *koStatement READ koStatement CONSTANT)
Q_PROPERTY(ConditionListModel *conditionListModel READ conditionListModel CONSTANT)
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)
public: public:
explicit ConnectionModelBackendDelegate(ConnectionModel *model); explicit ConnectionModelBackendDelegate(ConnectionModel *model);
using ActionType = ConnectionModelStatementDelegate::ActionType; void update() override;
Q_INVOKABLE void changeActionType(QmlDesigner::ConnectionModelStatementDelegate::ActionType actionType);
Q_INVOKABLE void addCondition();
Q_INVOKABLE void removeCondition();
Q_INVOKABLE void addElse();
Q_INVOKABLE void removeElse();
Q_INVOKABLE void setNewSource(const QString &newSource);
void setCurrentRow(int i); void setCurrentRow(int i);
void update();
Q_INVOKABLE void jumpToCode(); PropertyTreeModelDelegate *signal();
signals: signals:
void currentRowChanged(); void currentRowChanged();
void actionTypeChanged();
void hasConditionChanged(); private slots:
void hasElseChanged(); void handleTargetChanged();
void sourceChanged();
void popupShouldClose();
void popupShouldOpen();
private: private:
AbstractProperty getSourceProperty() const override;
void setPropertySource(const QString &source) override;
SignalHandlerProperty getSignalHandlerProperty() const;
int currentRow() const; int currentRow() const;
void handleException();
bool hasCondition() const;
bool hasElse() const;
void setHasCondition(bool b);
void setHasElse(bool b);
ActionType actionType() const;
PropertyTreeModelDelegate *signal();
ConnectionModelStatementDelegate *okStatement();
ConnectionModelStatementDelegate *koStatement();
ConditionListModel *conditionListModel();
QString indentedSource() const;
QString source() const;
void setSource(const QString &source);
PropertyTreeModel *propertyTreeModel();
PropertyListProxyModel *propertyListProxyModel();
void setupCondition();
void setupHandlerAndStatements();
void handleTargetChanged();
void handleOkStatementChanged();
void handleKOStatementChanged();
void handleConditionChanged();
void commitNewSource(const QString &source);
ActionType m_actionType;
QString m_exceptionError;
int m_currentRow = -1; int m_currentRow = -1;
ConnectionEditorStatements::Handler m_handler;
PropertyTreeModelDelegate m_signalDelegate; PropertyTreeModelDelegate m_signalDelegate;
ConnectionModelStatementDelegate m_okStatementDelegate;
ConnectionModelStatementDelegate m_koStatementDelegate;
ConditionListModel m_conditionListModel;
bool m_hasCondition = false;
bool m_hasElse = false;
QString m_source;
PropertyTreeModel m_propertyTreeModel;
PropertyListProxyModel m_propertyListProxyModel;
bool m_blockReflection = false;
QPointer<ConnectionModel> m_model = nullptr; QPointer<ConnectionModel> m_model = nullptr;
}; };

View File

@@ -83,10 +83,8 @@ public:
0, 0,
"DynamicPropertiesModelBackendDelegate"); "DynamicPropertiesModelBackendDelegate");
qmlRegisterType<ConnectionModelStatementDelegate>("ConnectionsEditorEditorBackend", // TODO: register in the property editor along with the ScriptEditorBackend
1, qmlRegisterType<StatementDelegate>("ScriptEditorBackend", 1, 0, "StatementDelegate");
0,
"ConnectionModelStatementDelegate");
qmlRegisterType<ConditionListModel>("ConnectionsEditorEditorBackend", 1, 0, "ConditionListModel"); qmlRegisterType<ConditionListModel>("ConnectionsEditorEditorBackend", 1, 0, "ConditionListModel");

View File

@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "dynamicpropertiesitem.h" #include "dynamicpropertiesitem.h"
#include "connectioneditorutils.h" #include <scripteditorutils.h>
#include <abstractproperty.h> #include <abstractproperty.h>
#include <abstractview.h> #include <abstractview.h>

View File

@@ -2,22 +2,23 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "dynamicpropertiesmodel.h" #include "dynamicpropertiesmodel.h"
#include "connectioneditorlogging.h"
#include "dynamicpropertiesitem.h" #include "dynamicpropertiesitem.h"
#include "connectioneditorutils.h"
#include <abstractproperty.h> #include <abstractproperty.h>
#include <bindingproperty.h> #include <bindingproperty.h>
#include <modelfwd.h> #include <modelfwd.h>
#include <rewritertransaction.h>
#include <rewritingexception.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <variantproperty.h>
#include <qmlchangeset.h> #include <qmlchangeset.h>
#include <qmldesignerconstants.h> #include <qmldesignerconstants.h>
#include <qmldesignerplugin.h> #include <qmldesignerplugin.h>
#include <qmlobjectnode.h> #include <qmlobjectnode.h>
#include <qmltimeline.h> #include <qmltimeline.h>
#include <rewritertransaction.h>
#include <rewritingexception.h>
#include <scripteditorutils.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <variantproperty.h>
#include <optional> #include <optional>

View File

@@ -27,7 +27,7 @@
#include "bindingproperty.h" #include "bindingproperty.h"
#include "propertyeditorvalue.h" #include "propertyeditorvalue.h"
#include "connectioneditorutils.h" #include <scripteditorutils.h>
#include <dynamicpropertiesmodel.h> #include <dynamicpropertiesmodel.h>

View File

@@ -163,8 +163,8 @@ std::vector<PropertyName> properityLists()
return result; return result;
} }
PropertyTreeModel::PropertyTreeModel(ConnectionView *view) PropertyTreeModel::PropertyTreeModel(AbstractView *view)
: m_connectionView(view) : m_view(view)
{} {}
void PropertyTreeModel::resetModel() void PropertyTreeModel::resetModel()
@@ -257,7 +257,7 @@ Qt::ItemFlags PropertyTreeModel::flags(const QModelIndex &) const
QModelIndex PropertyTreeModel::index(int row, int column, const QModelIndex &parent) const QModelIndex PropertyTreeModel::index(int row, int column, const QModelIndex &parent) const
{ {
auto internalId = parent.internalId(); auto internalId = parent.internalId();
if (!m_connectionView->isAttached()) if (!m_view->isAttached())
return {}; return {};
if (!parent.isValid()) if (!parent.isValid())
@@ -337,7 +337,7 @@ QPersistentModelIndex PropertyTreeModel::indexForInternalIdAndRow(quintptr inter
int PropertyTreeModel::rowCount(const QModelIndex &parent) const int PropertyTreeModel::rowCount(const QModelIndex &parent) const
{ {
if (!m_connectionView->isAttached() || parent.column() > 0) if (!m_view->isAttached() || parent.column() > 0)
return 0; return 0;
if (!parent.isValid()) if (!parent.isValid())
@@ -404,10 +404,10 @@ const std::vector<PropertyName> PropertyTreeModel::getProperties(const ModelNode
ModelNode PropertyTreeModel::getModelNodeForId(const QString &id) const ModelNode PropertyTreeModel::getModelNodeForId(const QString &id) const
{ {
if (!m_connectionView->isAttached()) if (!m_view->isAttached())
return {}; return {};
return m_connectionView->modelNodeForId(id); return m_view->modelNodeForId(id);
} }
QModelIndex PropertyTreeModel::ensureModelIndex(const ModelNode &node, int row) const QModelIndex PropertyTreeModel::ensureModelIndex(const ModelNode &node, int row) const
@@ -473,10 +473,10 @@ void PropertyTreeModel::testModel()
const QList<ModelNode> PropertyTreeModel::allModelNodesWithIdsSortedByDisplayName() const const QList<ModelNode> PropertyTreeModel::allModelNodesWithIdsSortedByDisplayName() const
{ {
if (!m_connectionView->isAttached()) if (!m_view->isAttached())
return {}; return {};
return Utils::sorted(ModelUtils::allModelNodesWithId(m_connectionView), return Utils::sorted(ModelUtils::allModelNodesWithId(m_view),
[](const ModelNode &lhs, const ModelNode &rhs) { [](const ModelNode &lhs, const ModelNode &rhs) {
return lhs.displayName() < rhs.displayName(); return lhs.displayName() < rhs.displayName();
}); });
@@ -882,7 +882,7 @@ QString PropertyListProxyModel::parentName() const
return m_treeModel->data(m_parentIndex, PropertyTreeModel::UserRoles::PropertyNameRole).toString(); return m_treeModel->data(m_parentIndex, PropertyTreeModel::UserRoles::PropertyNameRole).toString();
} }
PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *view) PropertyTreeModelDelegate::PropertyTreeModelDelegate(AbstractView *view)
: m_model(view) : m_model(view)
{ {
connect(&m_nameCombboBox, &StudioQmlComboBoxBackend::activated, this, [this] { connect(&m_nameCombboBox, &StudioQmlComboBoxBackend::activated, this, [this] {

View File

@@ -49,7 +49,8 @@ public:
BoolType BoolType
}; };
PropertyTreeModel(ConnectionView *view); // PropertyTreeModel(ConnectionView *view);
PropertyTreeModel(AbstractView *view);
void resetModel(); void resetModel();
@@ -120,7 +121,7 @@ private:
const PropertyMetaInfo &metaInfo, const PropertyMetaInfo &metaInfo,
bool recursive) const; bool recursive) const;
ConnectionView *m_connectionView; AbstractView *m_view;
mutable std::set<DataCacheItem> m_indexCache; mutable std::set<DataCacheItem> m_indexCache;
mutable std::vector<DataCacheItem> m_indexHash; mutable std::vector<DataCacheItem> m_indexHash;
@@ -187,7 +188,7 @@ class PropertyTreeModelDelegate : public QObject
Q_PROPERTY(StudioQmlComboBoxBackend *id READ idCombboBox CONSTANT) Q_PROPERTY(StudioQmlComboBoxBackend *id READ idCombboBox CONSTANT)
public: public:
explicit PropertyTreeModelDelegate(ConnectionView *view); explicit PropertyTreeModelDelegate(AbstractView *view);
void setPropertyType(PropertyTreeModel::PropertyTypes type); void setPropertyType(PropertyTreeModel::PropertyTypes type);
void setup(const QString &id, const QString &name, bool *nameExists = nullptr); void setup(const QString &id, const QString &name, bool *nameExists = nullptr);
void setupNameComboBox(const QString &id, const QString &name, bool *nameExists); void setupNameComboBox(const QString &id, const QString &name, bool *nameExists);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,250 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "propertytreemodel.h"
#include "scripteditorstatements.h"
namespace QmlDesigner {
class ConditionListModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(bool valid READ valid NOTIFY validChanged)
Q_PROPERTY(bool empty READ empty NOTIFY emptyChanged)
Q_PROPERTY(QString error READ error NOTIFY errorChanged)
Q_PROPERTY(int errorIndex READ errorIndex NOTIFY errorIndexChanged)
public:
enum ConditionType { Intermediate, Invalid, Operator, Literal, Variable, Shadow };
Q_ENUM(ConditionType)
struct ConditionToken
{
ConditionType type;
QString value;
};
ConditionListModel(AbstractView *view);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void setup();
void setCondition(ScriptEditorStatements::MatchedCondition &condition);
ScriptEditorStatements::MatchedCondition &condition();
static ConditionToken tokenFromConditionToken(const ScriptEditorStatements::ConditionToken &token);
static ConditionToken tokenFromComparativeStatement(
const ScriptEditorStatements::ComparativeStatement &token);
Q_INVOKABLE void insertToken(int index, const QString &value);
Q_INVOKABLE void updateToken(int index, const QString &value);
Q_INVOKABLE void appendToken(const QString &value);
Q_INVOKABLE void removeToken(int index);
Q_INVOKABLE void insertIntermediateToken(int index, const QString &value);
Q_INVOKABLE void insertShadowToken(int index, const QString &value);
Q_INVOKABLE void setShadowToken(int index, const QString &value);
bool valid() const;
bool empty() const;
//for debugging
Q_INVOKABLE void command(const QString &string);
void setInvalid(const QString &errorMessage, int index = -1);
void setValid();
QString error() const;
int errorIndex() const;
Q_INVOKABLE bool operatorAllowed(int cursorPosition);
signals:
void validChanged();
void emptyChanged();
void conditionChanged();
void errorChanged();
void errorIndexChanged();
private:
void internalSetup();
ConditionToken valueToToken(const QString &value);
void resetModel();
int checkOrder() const;
void validateAndRebuildTokens();
void rebuildTokens();
ScriptEditorStatements::ConditionToken toOperatorStatement(const ConditionToken &token);
ScriptEditorStatements::ComparativeStatement toStatement(const ConditionToken &token);
AbstractView *m_view = nullptr;
ScriptEditorStatements::MatchedCondition &m_condition;
QList<ConditionToken> m_tokens;
bool m_valid = false;
QString m_errorMessage;
int m_errorIndex = -1;
};
class StatementDelegate : public QObject
{
Q_OBJECT
public:
explicit StatementDelegate(AbstractView *view);
enum ActionType { CallFunction, Assign, ChangeState, SetProperty, PrintMessage, Custom };
Q_ENUM(ActionType)
Q_PROPERTY(ActionType actionType READ actionType NOTIFY actionTypeChanged)
Q_PROPERTY(PropertyTreeModelDelegate *function READ function CONSTANT)
Q_PROPERTY(PropertyTreeModelDelegate *lhs READ lhs CONSTANT)
Q_PROPERTY(PropertyTreeModelDelegate *rhsAssignment READ rhsAssignment CONSTANT)
Q_PROPERTY(StudioQmlTextBackend *stringArgument READ stringArgument CONSTANT)
Q_PROPERTY(StudioQmlComboBoxBackend *states READ states CONSTANT)
Q_PROPERTY(StudioQmlComboBoxBackend *stateTargets READ stateTargets CONSTANT)
void setActionType(ActionType type);
void setup();
void setStatement(ScriptEditorStatements::MatchedStatement &statement);
ScriptEditorStatements::MatchedStatement &statement();
signals:
void actionTypeChanged();
void statementChanged();
private:
ActionType actionType() const;
PropertyTreeModelDelegate *function();
PropertyTreeModelDelegate *lhs();
PropertyTreeModelDelegate *rhsAssignment();
StudioQmlTextBackend *stringArgument();
StudioQmlComboBoxBackend *stateTargets();
StudioQmlComboBoxBackend *states();
void handleFunctionChanged();
void handleLhsChanged();
void handleRhsAssignmentChanged();
void handleStringArgumentChanged();
void handleStateChanged();
void handleStateTargetsChanged();
void setupAssignment();
void setupSetProperty();
void setupCallFunction();
void setupChangeState();
void setupStates();
void setupPrintMessage();
void setupPropertyType();
QString baseStateName() const;
ActionType m_actionType;
PropertyTreeModelDelegate m_functionDelegate;
PropertyTreeModelDelegate m_lhsDelegate;
PropertyTreeModelDelegate m_rhsAssignmentDelegate;
ScriptEditorStatements::MatchedStatement &m_statement;
AbstractView *m_view = nullptr;
StudioQmlTextBackend m_stringArgument;
StudioQmlComboBoxBackend m_stateTargets;
StudioQmlComboBoxBackend m_states;
};
class ScriptEditorBackend : public QObject
{
Q_OBJECT
Q_PROPERTY(ActionType actionType READ actionType NOTIFY actionTypeChanged)
Q_PROPERTY(StatementDelegate *okStatement READ okStatement CONSTANT)
Q_PROPERTY(StatementDelegate *koStatement READ koStatement CONSTANT)
Q_PROPERTY(ConditionListModel *conditionListModel READ conditionListModel CONSTANT)
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)
public:
explicit ScriptEditorBackend(AbstractView *view);
using ActionType = StatementDelegate::ActionType;
Q_INVOKABLE void changeActionType(QmlDesigner::StatementDelegate::ActionType actionType);
Q_INVOKABLE void addCondition();
Q_INVOKABLE void removeCondition();
Q_INVOKABLE void addElse();
Q_INVOKABLE void removeElse();
Q_INVOKABLE void setNewSource(const QString &newSource);
Q_INVOKABLE virtual void update();
Q_INVOKABLE void jumpToCode();
signals:
void actionTypeChanged();
void hasConditionChanged();
void hasElseChanged();
void sourceChanged();
void popupShouldClose();
void popupShouldOpen();
protected:
bool blockReflection() const;
;
private:
virtual AbstractProperty getSourceProperty() const;
virtual void setPropertySource(const QString &source);
BindingProperty getBindingProperty() const;
void handleException();
bool hasCondition() const;
bool hasElse() const;
void setHasCondition(bool b);
void setHasElse(bool b);
ActionType actionType() const;
StatementDelegate *okStatement();
StatementDelegate *koStatement();
ConditionListModel *conditionListModel();
QString indentedSource() const;
QString source() const;
void setSource(const QString &source);
PropertyTreeModel *propertyTreeModel();
PropertyListProxyModel *propertyListProxyModel();
void setupCondition();
void setupHandlerAndStatements();
void handleOkStatementChanged();
void handleKOStatementChanged();
void handleConditionChanged();
void commitNewSource(const QString &source);
ActionType m_actionType;
QString m_exceptionError;
ScriptEditorStatements::Handler m_handler;
StatementDelegate m_okStatementDelegate;
StatementDelegate m_koStatementDelegate;
ConditionListModel m_conditionListModel;
bool m_hasCondition = false;
bool m_hasElse = false;
QString m_source;
PropertyTreeModel m_propertyTreeModel;
PropertyListProxyModel m_propertyListProxyModel;
bool m_blockReflection = false;
QPointer<AbstractView> m_view = nullptr;
};
} // namespace QmlDesigner

View File

@@ -1,10 +1,10 @@
// Copyright (C) 2023 The Qt Company Ltd. // Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "connectioneditorevaluator.h" #include "scripteditorevaluator.h"
#include "connectioneditorutils.h"
#include "qmljs/parser/qmljsast_p.h" #include "qmljs/parser/qmljsast_p.h"
#include "qmljs/qmljsdocument.h" #include "qmljs/qmljsdocument.h"
#include "scripteditorutils.h"
#include <utils/ranges.h> #include <utils/ranges.h>
@@ -14,10 +14,10 @@ using namespace QmlDesigner;
using QmlJS::AST::Node; using QmlJS::AST::Node;
using Kind = Node::Kind; using Kind = Node::Kind;
using ConnectionEditorStatements::ConditionalStatement; using ScriptEditorStatements::ConditionalStatement;
using ConnectionEditorStatements::ConditionToken; using ScriptEditorStatements::ConditionToken;
using ConnectionEditorStatements::MatchedCondition; using ScriptEditorStatements::MatchedCondition;
using ConnectionEditorStatements::MatchedStatement; using ScriptEditorStatements::MatchedStatement;
namespace { namespace {
enum class TrackingArea { No, Condition, Ok, Ko }; enum class TrackingArea { No, Condition, Ok, Ko };
@@ -255,14 +255,14 @@ protected:
void throwRecursionDepthError() override void throwRecursionDepthError() override
{ {
checkValidityAndReturn(false, "Recursion depth problem"); checkValidityAndReturn(false, "Recursion depth problem");
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Recursion depth error"; qCWarning(ScriptEditorLog) << __FUNCTION__ << "Recursion depth error";
} }
void checkAndResetVariable() void checkAndResetVariable()
{ {
if (--m_depth == 0) { if (--m_depth == 0) {
m_condition.statements.push_back( m_condition.statements.push_back(
ConnectionEditorStatements::Variable{m_identifier, m_fields.join(".")}); ScriptEditorStatements::Variable{m_identifier, m_fields.join(".")});
m_identifier.clear(); m_identifier.clear();
m_fields.clear(); m_fields.clear();
} }
@@ -300,7 +300,7 @@ private:
class RightHandVisitor : public QmlJS::AST::Visitor class RightHandVisitor : public QmlJS::AST::Visitor
{ {
public: public:
ConnectionEditorStatements::RightHandSide rhs() const { return m_rhs; } ScriptEditorStatements::RightHandSide rhs() const { return m_rhs; }
void reset() void reset()
{ {
@@ -318,48 +318,48 @@ public:
if (!isValid()) if (!isValid())
return false; return false;
return ConnectionEditorStatements::isLiteralType(rhs()); return ScriptEditorStatements::isLiteralType(rhs());
} }
bool couldBeLHS() const bool couldBeLHS() const
{ {
if (!isValid()) if (!isValid())
return false; return false;
return std::holds_alternative<ConnectionEditorStatements::Variable>(m_rhs); return std::holds_alternative<ScriptEditorStatements::Variable>(m_rhs);
} }
inline bool couldBeVariable() const { return couldBeLHS(); } inline bool couldBeVariable() const { return couldBeLHS(); }
ConnectionEditorStatements::Literal literal() const ScriptEditorStatements::Literal literal() const
{ {
if (!isLiteralType()) if (!isLiteralType())
return {}; return {};
return std::visit( return std::visit(
Overload{[](const bool &var) -> ConnectionEditorStatements::Literal { return var; }, Overload{[](const bool &var) -> ScriptEditorStatements::Literal { return var; },
[](const double &var) -> ConnectionEditorStatements::Literal { return var; }, [](const double &var) -> ScriptEditorStatements::Literal { return var; },
[](const QString &var) -> ConnectionEditorStatements::Literal { return var; }, [](const QString &var) -> ScriptEditorStatements::Literal { return var; },
[](const auto &) -> ConnectionEditorStatements::Literal { return false; }}, [](const auto &) -> ScriptEditorStatements::Literal { return false; }},
m_rhs); m_rhs);
} }
ConnectionEditorStatements::Variable lhs() const ScriptEditorStatements::Variable lhs() const
{ {
if (!isValid()) if (!isValid())
return {}; return {};
if (auto rhs = std::get_if<ConnectionEditorStatements::Variable>(&m_rhs)) if (auto rhs = std::get_if<ScriptEditorStatements::Variable>(&m_rhs))
return *rhs; return *rhs;
return {}; return {};
} }
ConnectionEditorStatements::Variable variable() const ScriptEditorStatements::Variable variable() const
{ {
if (!isValid()) if (!isValid())
return {}; return {};
if (auto rhs = std::get_if<ConnectionEditorStatements::Variable>(&m_rhs)) if (auto rhs = std::get_if<ScriptEditorStatements::Variable>(&m_rhs))
return *rhs; return *rhs;
return {}; return {};
@@ -478,13 +478,13 @@ protected:
void throwRecursionDepthError() override void throwRecursionDepthError() override
{ {
setFailed(); setFailed();
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Recursion depth error"; qCWarning(ScriptEditorLog) << __FUNCTION__ << "Recursion depth error";
} }
void checkAndResetCal() void checkAndResetCal()
{ {
if (--m_depth == 0) { if (--m_depth == 0) {
m_rhs = ConnectionEditorStatements::MatchedFunction{m_identifier, m_fields.join(".")}; m_rhs = ScriptEditorStatements::MatchedFunction{m_identifier, m_fields.join(".")};
m_specified = true; m_specified = true;
m_identifier.clear(); m_identifier.clear();
@@ -495,7 +495,7 @@ protected:
void checkAndResetNonCal() void checkAndResetNonCal()
{ {
if (--m_depth == 0) { if (--m_depth == 0) {
m_rhs = ConnectionEditorStatements::Variable{m_identifier, m_fields.join(".")}; m_rhs = ScriptEditorStatements::Variable{m_identifier, m_fields.join(".")};
m_specified = true; m_specified = true;
m_identifier.clear(); m_identifier.clear();
@@ -513,22 +513,23 @@ private:
int m_depth = 0; int m_depth = 0;
QString m_identifier; QString m_identifier;
QStringList m_fields; QStringList m_fields;
ConnectionEditorStatements::RightHandSide m_rhs; ScriptEditorStatements::RightHandSide m_rhs;
}; };
MatchedStatement checkForStateSet(const MatchedStatement &currentState) MatchedStatement checkForStateSet(const MatchedStatement &currentState)
{ {
using namespace ConnectionEditorStatements; using namespace ScriptEditorStatements;
return std::visit( return std::visit(Overload{[](const PropertySet &propertySet) -> MatchedStatement {
Overload{[](const PropertySet &propertySet) -> MatchedStatement { if (propertySet.lhs.nodeId.size()
if (propertySet.lhs.nodeId.size() && propertySet.lhs.propertyName == u"state" && propertySet.lhs.propertyName == u"state"
&& std::holds_alternative<QString>(propertySet.rhs)) && std::holds_alternative<QString>(propertySet.rhs))
return StateSet{propertySet.lhs.nodeId, return StateSet{propertySet.lhs.nodeId,
ConnectionEditorStatements::toString(propertySet.rhs)}; ScriptEditorStatements::toString(
return propertySet; propertySet.rhs)};
}, return propertySet;
[](const auto &pSet) -> MatchedStatement { return pSet; }}, },
currentState); [](const auto &pSet) -> MatchedStatement { return pSet; }},
currentState);
} }
class ConsoleLogEvaluator : public QmlJS::AST::Visitor class ConsoleLogEvaluator : public QmlJS::AST::Visitor
@@ -536,9 +537,9 @@ class ConsoleLogEvaluator : public QmlJS::AST::Visitor
public: public:
bool isValid() { return m_completed; } bool isValid() { return m_completed; }
ConnectionEditorStatements::ConsoleLog expression() ScriptEditorStatements::ConsoleLog expression()
{ {
return ConnectionEditorStatements::ConsoleLog{m_arg}; return ScriptEditorStatements::ConsoleLog{m_arg};
} }
protected: protected:
@@ -618,14 +619,14 @@ protected:
void throwRecursionDepthError() override void throwRecursionDepthError() override
{ {
m_failed = true; m_failed = true;
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Recursion depth error"; qCWarning(ScriptEditorLog) << __FUNCTION__ << "Recursion depth error";
} }
private: private:
bool m_failed = false; bool m_failed = false;
bool m_completed = false; bool m_completed = false;
int m_stepId = 0; int m_stepId = 0;
ConnectionEditorStatements::RightHandSide m_arg; ScriptEditorStatements::RightHandSide m_arg;
}; };
struct StatementReply struct StatementReply
@@ -650,10 +651,10 @@ struct StatementReply
} // namespace } // namespace
class QmlDesigner::ConnectionEditorEvaluatorPrivate class QmlDesigner::ScriptEditorEvaluatorPrivate
{ {
friend class ConnectionEditorEvaluator; friend class ScriptEditorEvaluator;
using Status = ConnectionEditorEvaluator::Status; using Status = ScriptEditorEvaluator::Status;
public: public:
bool checkValidityAndReturn(bool valid, const QString &parseError = {}); bool checkValidityAndReturn(bool valid, const QString &parseError = {});
@@ -731,28 +732,28 @@ private:
QString m_errorString; QString m_errorString;
Status m_checkStatus = Status::UnStarted; Status m_checkStatus = Status::UnStarted;
QList<NodeStatus> m_nodeHierarchy; QList<NodeStatus> m_nodeHierarchy;
ConnectionEditorStatements::Handler m_handler; ScriptEditorStatements::Handler m_handler;
}; };
ConnectionEditorEvaluator::ConnectionEditorEvaluator() ScriptEditorEvaluator::ScriptEditorEvaluator()
: d(std::make_unique<ConnectionEditorEvaluatorPrivate>()) : d(std::make_unique<ScriptEditorEvaluatorPrivate>())
{} {}
ConnectionEditorEvaluator::~ConnectionEditorEvaluator() {} ScriptEditorEvaluator::~ScriptEditorEvaluator() {}
ConnectionEditorEvaluator::Status ConnectionEditorEvaluator::status() const ScriptEditorEvaluator::Status ScriptEditorEvaluator::status() const
{ {
return d->m_checkStatus; return d->m_checkStatus;
} }
ConnectionEditorStatements::Handler ConnectionEditorEvaluator::resultNode() const ScriptEditorStatements::Handler ScriptEditorEvaluator::resultNode() const
{ {
return (d->m_checkStatus == Succeeded) ? d->m_handler : ConnectionEditorStatements::EmptyBlock{}; return (d->m_checkStatus == Succeeded) ? d->m_handler : ScriptEditorStatements::EmptyBlock{};
} }
QString ConnectionEditorEvaluator::getDisplayStringForType(const QString &statement) QString ScriptEditorEvaluator::getDisplayStringForType(const QString &statement)
{ {
ConnectionEditorEvaluator evaluator; ScriptEditorEvaluator evaluator;
QmlJS::Document::MutablePtr newDoc = QmlJS::Document::create(Utils::FilePath::fromString( QmlJS::Document::MutablePtr newDoc = QmlJS::Document::create(Utils::FilePath::fromString(
"<expression>"), "<expression>"),
QmlJS::Dialect::JavaScript); QmlJS::Dialect::JavaScript);
@@ -761,23 +762,23 @@ QString ConnectionEditorEvaluator::getDisplayStringForType(const QString &statem
newDoc->parseJavaScript(); newDoc->parseJavaScript();
if (!newDoc->isParsedCorrectly()) if (!newDoc->isParsedCorrectly())
return ConnectionEditorStatements::CUSTOM_DISPLAY_NAME; return ScriptEditorStatements::CUSTOM_DISPLAY_NAME;
newDoc->ast()->accept(&evaluator); newDoc->ast()->accept(&evaluator);
const bool valid = evaluator.status() == ConnectionEditorEvaluator::Succeeded; const bool valid = evaluator.status() == ScriptEditorEvaluator::Succeeded;
if (!valid) if (!valid)
return ConnectionEditorStatements::CUSTOM_DISPLAY_NAME; return ScriptEditorStatements::CUSTOM_DISPLAY_NAME;
auto result = evaluator.resultNode(); auto result = evaluator.resultNode();
return QmlDesigner::ConnectionEditorStatements::toDisplayName(result); return QmlDesigner::ScriptEditorStatements::toDisplayName(result);
} }
ConnectionEditorStatements::Handler ConnectionEditorEvaluator::parseStatement(const QString &statement) ScriptEditorStatements::Handler ScriptEditorEvaluator::parseStatement(const QString &statement)
{ {
ConnectionEditorEvaluator evaluator; ScriptEditorEvaluator evaluator;
QmlJS::Document::MutablePtr newDoc = QmlJS::Document::create(Utils::FilePath::fromString( QmlJS::Document::MutablePtr newDoc = QmlJS::Document::create(Utils::FilePath::fromString(
"<expression>"), "<expression>"),
QmlJS::Dialect::JavaScript); QmlJS::Dialect::JavaScript);
@@ -786,19 +787,19 @@ ConnectionEditorStatements::Handler ConnectionEditorEvaluator::parseStatement(co
newDoc->parseJavaScript(); newDoc->parseJavaScript();
if (!newDoc->isParsedCorrectly()) if (!newDoc->isParsedCorrectly())
return ConnectionEditorStatements::EmptyBlock{}; return ScriptEditorStatements::EmptyBlock{};
newDoc->ast()->accept(&evaluator); newDoc->ast()->accept(&evaluator);
const bool valid = evaluator.status() == ConnectionEditorEvaluator::Succeeded; const bool valid = evaluator.status() == ScriptEditorEvaluator::Succeeded;
if (!valid) if (!valid)
return ConnectionEditorStatements::EmptyBlock{}; return ScriptEditorStatements::EmptyBlock{};
return evaluator.resultNode(); return evaluator.resultNode();
} }
bool ConnectionEditorEvaluator::preVisit(Node *node) bool ScriptEditorEvaluator::preVisit(Node *node)
{ {
if (d->m_nodeHierarchy.size()) { if (d->m_nodeHierarchy.size()) {
NodeStatus &parentNode = d->m_nodeHierarchy.last(); NodeStatus &parentNode = d->m_nodeHierarchy.last();
@@ -832,7 +833,7 @@ bool ConnectionEditorEvaluator::preVisit(Node *node)
} }
} }
void ConnectionEditorEvaluator::postVisit(QmlJS::AST::Node *node) void ScriptEditorEvaluator::postVisit(QmlJS::AST::Node *node)
{ {
if (d->m_nodeHierarchy.isEmpty()) { if (d->m_nodeHierarchy.isEmpty()) {
d->checkValidityAndReturn(false, "Unexpected post visiting"); d->checkValidityAndReturn(false, "Unexpected post visiting");
@@ -859,18 +860,18 @@ void ConnectionEditorEvaluator::postVisit(QmlJS::AST::Node *node)
} }
} }
bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::Program *program) bool ScriptEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::Program *program)
{ {
d->setStatus(UnFinished); d->setStatus(UnFinished);
d->setTrackingArea(false, 0); d->setTrackingArea(false, 0);
d->m_ifStatement = 0; d->m_ifStatement = 0;
d->m_consoleLogCount = 0; d->m_consoleLogCount = 0;
d->m_consoleIdentifierCount = 0; d->m_consoleIdentifierCount = 0;
d->m_handler = ConnectionEditorStatements::EmptyBlock{}; d->m_handler = ScriptEditorStatements::EmptyBlock{};
return true; return true;
} }
bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::IfStatement *ifStatement) bool ScriptEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::IfStatement *ifStatement)
{ {
if (d->m_ifStatement++) if (d->m_ifStatement++)
return d->checkValidityAndReturn(false, "Nested if conditions are not supported"); return d->checkValidityAndReturn(false, "Nested if conditions are not supported");
@@ -885,7 +886,7 @@ bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::IfStatement *
return d->checkValidityAndReturn(true); return d->checkValidityAndReturn(true);
} }
bool ConnectionEditorEvaluator::visit(QmlJS::AST::IdentifierExpression *identifier) bool ScriptEditorEvaluator::visit(QmlJS::AST::IdentifierExpression *identifier)
{ {
if (d->parentNodeStatus() == Kind::Kind_FieldMemberExpression) if (d->parentNodeStatus() == Kind::Kind_FieldMemberExpression)
if (d->m_consoleLogCount) if (d->m_consoleLogCount)
@@ -896,7 +897,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::IdentifierExpression *identifi
return d->checkValidityAndReturn(true); return d->checkValidityAndReturn(true);
} }
bool ConnectionEditorEvaluator::visit(QmlJS::AST::BinaryExpression *binaryExpression) bool ScriptEditorEvaluator::visit(QmlJS::AST::BinaryExpression *binaryExpression)
{ {
if (d->isInIfCondition()) { if (d->isInIfCondition()) {
if (binaryExpression->op == QSOperator::Assign) if (binaryExpression->op == QSOperator::Assign)
@@ -923,7 +924,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::BinaryExpression *binaryExpres
return false; return false;
} else { } else {
MatchedStatement *currentStatement = d->currentStatement(); MatchedStatement *currentStatement = d->currentStatement();
if (currentStatement && ConnectionEditorStatements::isEmptyStatement(*currentStatement) if (currentStatement && ScriptEditorStatements::isEmptyStatement(*currentStatement)
&& d->parentNodeStatus().childId() == 0) { && d->parentNodeStatus().childId() == 0) {
if (binaryExpression->op == QSOperator::Assign) { if (binaryExpression->op == QSOperator::Assign) {
RightHandVisitor variableVisitor; RightHandVisitor variableVisitor;
@@ -932,16 +933,16 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::BinaryExpression *binaryExpres
if (!variableVisitor.couldBeLHS()) if (!variableVisitor.couldBeLHS())
return d->checkValidityAndReturn(false, "Invalid left hand."); return d->checkValidityAndReturn(false, "Invalid left hand.");
ConnectionEditorStatements::Variable lhs = variableVisitor.lhs(); ScriptEditorStatements::Variable lhs = variableVisitor.lhs();
variableVisitor.reset(); variableVisitor.reset();
binaryExpression->right->accept(&variableVisitor); binaryExpression->right->accept(&variableVisitor);
if (variableVisitor.couldBeLHS()) { if (variableVisitor.couldBeLHS()) {
ConnectionEditorStatements::Assignment assignment{lhs, variableVisitor.variable()}; ScriptEditorStatements::Assignment assignment{lhs, variableVisitor.variable()};
*currentStatement = assignment; *currentStatement = assignment;
} else if (variableVisitor.isLiteralType()) { } else if (variableVisitor.isLiteralType()) {
ConnectionEditorStatements::PropertySet propSet{lhs, variableVisitor.literal()}; ScriptEditorStatements::PropertySet propSet{lhs, variableVisitor.literal()};
*currentStatement = propSet; *currentStatement = propSet;
} else { } else {
return d->checkValidityAndReturn(false, "Invalid RHS"); return d->checkValidityAndReturn(false, "Invalid RHS");
@@ -955,7 +956,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::BinaryExpression *binaryExpres
return d->checkValidityAndReturn(true); return d->checkValidityAndReturn(true);
} }
bool ConnectionEditorEvaluator::visit(QmlJS::AST::FieldMemberExpression *fieldExpression) bool ScriptEditorEvaluator::visit(QmlJS::AST::FieldMemberExpression *fieldExpression)
{ {
if (d->parentNodeStatus() == Kind::Kind_CallExpression && fieldExpression->name == u"log") if (d->parentNodeStatus() == Kind::Kind_CallExpression && fieldExpression->name == u"log")
d->m_consoleLogCount++; d->m_consoleLogCount++;
@@ -965,7 +966,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::FieldMemberExpression *fieldEx
return d->checkValidityAndReturn(true); return d->checkValidityAndReturn(true);
} }
bool ConnectionEditorEvaluator::visit(QmlJS::AST::CallExpression *callExpression) bool ScriptEditorEvaluator::visit(QmlJS::AST::CallExpression *callExpression)
{ {
if (d->isInIfCondition()) if (d->isInIfCondition())
return d->checkValidityAndReturn(false, "Functions are not allowd in the expressions"); return d->checkValidityAndReturn(false, "Functions are not allowd in the expressions");
@@ -974,7 +975,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::CallExpression *callExpression
if (!currentStatement) if (!currentStatement)
return d->checkValidityAndReturn(false, "Invalid place to call an expression"); return d->checkValidityAndReturn(false, "Invalid place to call an expression");
if (ConnectionEditorStatements::isEmptyStatement(*currentStatement)) { if (ScriptEditorStatements::isEmptyStatement(*currentStatement)) {
if (d->parentNodeStatus().childId() == 0) { if (d->parentNodeStatus().childId() == 0) {
ConsoleLogEvaluator logEvaluator; ConsoleLogEvaluator logEvaluator;
callExpression->accept(&logEvaluator); callExpression->accept(&logEvaluator);
@@ -985,8 +986,8 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::CallExpression *callExpression
callExpression->accept(&callVisitor); callExpression->accept(&callVisitor);
if (callVisitor.isValid()) { if (callVisitor.isValid()) {
ConnectionEditorStatements::RightHandSide rhs = callVisitor.rhs(); ScriptEditorStatements::RightHandSide rhs = callVisitor.rhs();
if (auto rhs_ = std::get_if<ConnectionEditorStatements::MatchedFunction>(&rhs)) if (auto rhs_ = std::get_if<ScriptEditorStatements::MatchedFunction>(&rhs))
*currentStatement = *rhs_; *currentStatement = *rhs_;
else else
return d->checkValidityAndReturn(false, "Invalid Matched Function type."); return d->checkValidityAndReturn(false, "Invalid Matched Function type.");
@@ -999,7 +1000,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::CallExpression *callExpression
return d->checkValidityAndReturn(true); return d->checkValidityAndReturn(true);
} }
bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::Block *block) bool ScriptEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::Block *block)
{ {
Kind parentKind = d->parentNodeStatus(); Kind parentKind = d->parentNodeStatus();
@@ -1013,7 +1014,7 @@ bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::Block *block)
return d->checkValidityAndReturn(false, "Block count ptoblem"); return d->checkValidityAndReturn(false, "Block count ptoblem");
} }
bool ConnectionEditorEvaluator::visit(QmlJS::AST::ArgumentList *arguments) bool ScriptEditorEvaluator::visit(QmlJS::AST::ArgumentList *arguments)
{ {
if (d->trackingArea() == TrackingArea::Condition) if (d->trackingArea() == TrackingArea::Condition)
return d->checkValidityAndReturn(false, "Arguments are not supported in if condition"); return d->checkValidityAndReturn(false, "Arguments are not supported in if condition");
@@ -1022,7 +1023,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::ArgumentList *arguments)
if (!currentStatement) if (!currentStatement)
return d->checkValidityAndReturn(false, "No statement found for argument"); return d->checkValidityAndReturn(false, "No statement found for argument");
if (!ConnectionEditorStatements::isConsoleLog(*currentStatement)) if (!ScriptEditorStatements::isConsoleLog(*currentStatement))
return d->checkValidityAndReturn(false, "Arguments are only supported for console.log"); return d->checkValidityAndReturn(false, "Arguments are only supported for console.log");
if (d->m_acceptLogArgument && !arguments->next) if (d->m_acceptLogArgument && !arguments->next)
@@ -1031,13 +1032,13 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::ArgumentList *arguments)
return d->checkValidityAndReturn(false, "The only supported argument is in console.log"); return d->checkValidityAndReturn(false, "The only supported argument is in console.log");
} }
void ConnectionEditorEvaluator::endVisit([[maybe_unused]] QmlJS::AST::Program *program) void ScriptEditorEvaluator::endVisit([[maybe_unused]] QmlJS::AST::Program *program)
{ {
if (status() == UnFinished) if (status() == UnFinished)
d->setStatus(Succeeded); d->setStatus(Succeeded);
} }
void ConnectionEditorEvaluator::endVisit(QmlJS::AST::FieldMemberExpression *fieldExpression) void ScriptEditorEvaluator::endVisit(QmlJS::AST::FieldMemberExpression *fieldExpression)
{ {
if (status() != UnFinished) if (status() != UnFinished)
return; return;
@@ -1053,12 +1054,12 @@ void ConnectionEditorEvaluator::endVisit(QmlJS::AST::FieldMemberExpression *fiel
} }
} }
void ConnectionEditorEvaluator::endVisit([[maybe_unused]] QmlJS::AST::CallExpression *callExpression) void ScriptEditorEvaluator::endVisit([[maybe_unused]] QmlJS::AST::CallExpression *callExpression)
{ {
d->m_acceptLogArgument = false; d->m_acceptLogArgument = false;
} }
void ConnectionEditorEvaluator::endVisit(QmlJS::AST::IfStatement * /*ifStatement*/) void ScriptEditorEvaluator::endVisit(QmlJS::AST::IfStatement * /*ifStatement*/)
{ {
if (status() != UnFinished) if (status() != UnFinished)
return; return;
@@ -1071,7 +1072,7 @@ void ConnectionEditorEvaluator::endVisit(QmlJS::AST::IfStatement * /*ifStatement
} }
} }
void ConnectionEditorEvaluator::endVisit(QmlJS::AST::StatementList * /*statementList*/) void ScriptEditorEvaluator::endVisit(QmlJS::AST::StatementList * /*statementList*/)
{ {
if (status() != UnFinished) if (status() != UnFinished)
return; return;
@@ -1080,26 +1081,26 @@ void ConnectionEditorEvaluator::endVisit(QmlJS::AST::StatementList * /*statement
d->checkValidityAndReturn(false, "More than one statements are available."); d->checkValidityAndReturn(false, "More than one statements are available.");
} }
void ConnectionEditorEvaluator::throwRecursionDepthError() void ScriptEditorEvaluator::throwRecursionDepthError()
{ {
d->checkValidityAndReturn(false, "Recursion depth problem"); d->checkValidityAndReturn(false, "Recursion depth problem");
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Recursion depth error"; qCWarning(ScriptEditorLog) << __FUNCTION__ << "Recursion depth error";
} }
bool ConnectionEditorEvaluatorPrivate::checkValidityAndReturn(bool valid, const QString &parseError) bool ScriptEditorEvaluatorPrivate::checkValidityAndReturn(bool valid, const QString &parseError)
{ {
if (!valid) { if (!valid) {
if (m_checkStatus != Status::Failed) { if (m_checkStatus != Status::Failed) {
setStatus(Status::Failed); setStatus(Status::Failed);
m_errorString = parseError; m_errorString = parseError;
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Parse error" << parseError; qCWarning(ScriptEditorLog) << __FUNCTION__ << "Parse error" << parseError;
} }
} }
return m_checkStatus; return m_checkStatus;
} }
NodeStatus ConnectionEditorEvaluatorPrivate::nodeStatus(int reverseLevel) const NodeStatus ScriptEditorEvaluatorPrivate::nodeStatus(int reverseLevel) const
{ {
if (m_nodeHierarchy.size() > reverseLevel) if (m_nodeHierarchy.size() > reverseLevel)
return m_nodeHierarchy.at(m_nodeHierarchy.size() - reverseLevel - 1); return m_nodeHierarchy.at(m_nodeHierarchy.size() - reverseLevel - 1);

View File

@@ -3,7 +3,7 @@
#pragma once #pragma once
#include "connectioneditorstatements.h" #include "scripteditorstatements.h"
#include "qmldesigner_global.h" #include "qmldesigner_global.h"
@@ -11,21 +11,21 @@
namespace QmlDesigner { namespace QmlDesigner {
class ConnectionEditorEvaluatorPrivate; class ScriptEditorEvaluatorPrivate;
class QMLDESIGNER_EXPORT ConnectionEditorEvaluator : public QmlJS::AST::Visitor class QMLDESIGNER_EXPORT ScriptEditorEvaluator : public QmlJS::AST::Visitor
{ {
public: public:
enum Status { UnStarted, UnFinished, Succeeded, Failed }; enum Status { UnStarted, UnFinished, Succeeded, Failed };
ConnectionEditorEvaluator(); ScriptEditorEvaluator();
virtual ~ConnectionEditorEvaluator(); virtual ~ScriptEditorEvaluator();
Status status() const; Status status() const;
ConnectionEditorStatements::Handler resultNode() const; ScriptEditorStatements::Handler resultNode() const;
static QString getDisplayStringForType(const QString &statement); static QString getDisplayStringForType(const QString &statement);
static ConnectionEditorStatements::Handler parseStatement(const QString &statement); static ScriptEditorStatements::Handler parseStatement(const QString &statement);
protected: protected:
bool preVisit(QmlJS::AST::Node *node) override; bool preVisit(QmlJS::AST::Node *node) override;
@@ -49,7 +49,7 @@ protected:
void throwRecursionDepthError() override; void throwRecursionDepthError() override;
private: private:
std::unique_ptr<ConnectionEditorEvaluatorPrivate> d; std::unique_ptr<ScriptEditorEvaluatorPrivate> d;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -1,11 +1,11 @@
// Copyright (C) 2023 The Qt Company Ltd. // Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "connectioneditorstatements.h" #include "scripteditorstatements.h"
#include <QHash> #include <QHash>
using namespace QmlDesigner; using namespace QmlDesigner;
using namespace ConnectionEditorStatements; using namespace ScriptEditorStatements;
namespace { namespace {
template<typename... Ts> template<typename... Ts>
@@ -39,31 +39,31 @@ struct StringVisitor
return "Variable{" + var.nodeId + propertyName + "}"; return "Variable{" + var.nodeId + propertyName + "}";
} }
QString operator()(const ConnectionEditorStatements::MatchedFunction &func) QString operator()(const ScriptEditorStatements::MatchedFunction &func)
{ {
return "MatchedFunction{" + func.nodeId + "." + func.functionName + "}"; return "MatchedFunction{" + func.nodeId + "." + func.functionName + "}";
} }
QString operator()(const ConnectionEditorStatements::Assignment &assignment) QString operator()(const ScriptEditorStatements::Assignment &assignment)
{ {
return "Assignment{" + assignment.lhs.expression() + " = " + StringVisitor()(assignment.rhs) return "Assignment{" + assignment.lhs.expression() + " = " + StringVisitor()(assignment.rhs)
+ "}"; + "}";
} }
QString operator()(const ConnectionEditorStatements::PropertySet &propertySet) QString operator()(const ScriptEditorStatements::PropertySet &propertySet)
{ {
return "PropertySet{" + propertySet.lhs.expression() + " = " return "PropertySet{" + propertySet.lhs.expression() + " = "
+ std::visit(StringVisitor{}, propertySet.rhs) + "}"; + std::visit(StringVisitor{}, propertySet.rhs) + "}";
} }
QString operator()(const ConnectionEditorStatements::StateSet &stateSet) QString operator()(const ScriptEditorStatements::StateSet &stateSet)
{ {
return "StateSet{" + stateSet.nodeId + ".state = " + stateSet.stateName + "}"; return "StateSet{" + stateSet.nodeId + ".state = " + stateSet.stateName + "}";
} }
QString operator()(const ConnectionEditorStatements::EmptyBlock &) { return "EmptyBlock{}"; } QString operator()(const ScriptEditorStatements::EmptyBlock &) { return "EmptyBlock{}"; }
QString operator()(const ConnectionEditorStatements::ConsoleLog &consoleLog) QString operator()(const ScriptEditorStatements::ConsoleLog &consoleLog)
{ {
return "ConsoleLog{" + std::visit(StringVisitor{}, consoleLog.argument) + "}"; return "ConsoleLog{" + std::visit(StringVisitor{}, consoleLog.argument) + "}";
} }
@@ -92,7 +92,7 @@ struct StringVisitor
} }
} }
QString operator()(const ConnectionEditorStatements::MatchedCondition &matched) QString operator()(const ScriptEditorStatements::MatchedCondition &matched)
{ {
if (!matched.statements.size() && !matched.tokens.size()) if (!matched.statements.size() && !matched.tokens.size())
return "MatchedCondition{}"; return "MatchedCondition{}";
@@ -113,7 +113,7 @@ struct StringVisitor
return value; return value;
} }
QString operator()(const ConnectionEditorStatements::ConditionalStatement &conditional) QString operator()(const ScriptEditorStatements::ConditionalStatement &conditional)
{ {
QString value; QString value;
value.reserve(200); value.reserve(200);
@@ -130,7 +130,7 @@ struct StringVisitor
return value; return value;
} }
QString operator()(const ConnectionEditorStatements::MatchedStatement &conditional) QString operator()(const ScriptEditorStatements::MatchedStatement &conditional)
{ {
return std::visit(StringVisitor{}, conditional); return std::visit(StringVisitor{}, conditional);
} }
@@ -156,7 +156,7 @@ struct JSOverload
return var.nodeId + propertyName; return var.nodeId + propertyName;
} }
QString operator()(const ConnectionEditorStatements::MatchedFunction &func) QString operator()(const ScriptEditorStatements::MatchedFunction &func)
{ {
QString funcName; QString funcName;
if (func.functionName.size()) if (func.functionName.size())
@@ -165,31 +165,31 @@ struct JSOverload
return func.nodeId + funcName + "()"; return func.nodeId + funcName + "()";
} }
QString operator()(const ConnectionEditorStatements::Assignment &assignment) QString operator()(const ScriptEditorStatements::Assignment &assignment)
{ {
return JSOverload()(assignment.lhs) + " = " + JSOverload()(assignment.rhs); return JSOverload()(assignment.lhs) + " = " + JSOverload()(assignment.rhs);
} }
QString operator()(const ConnectionEditorStatements::PropertySet &propertySet) QString operator()(const ScriptEditorStatements::PropertySet &propertySet)
{ {
return JSOverload()(propertySet.lhs) + " = " + std::visit(JSOverload{}, propertySet.rhs); return JSOverload()(propertySet.lhs) + " = " + std::visit(JSOverload{}, propertySet.rhs);
} }
QString operator()(const ConnectionEditorStatements::StateSet &stateSet) QString operator()(const ScriptEditorStatements::StateSet &stateSet)
{ {
return stateSet.nodeId + ".state = " + stateSet.stateName; return stateSet.nodeId + ".state = " + stateSet.stateName;
} }
QString operator()(const ConnectionEditorStatements::EmptyBlock &) { return "{}"; } QString operator()(const ScriptEditorStatements::EmptyBlock &) { return "{}"; }
QString operator()(const ConnectionEditorStatements::ConsoleLog &consoleLog) QString operator()(const ScriptEditorStatements::ConsoleLog &consoleLog)
{ {
return "console.log(" + std::visit(JSOverload{}, consoleLog.argument) + ")"; return "console.log(" + std::visit(JSOverload{}, consoleLog.argument) + ")";
} }
QString operator()(const ConditionToken &token) { return toJavascript(token); } QString operator()(const ConditionToken &token) { return toJavascript(token); }
QString operator()(const ConnectionEditorStatements::MatchedCondition &matched) QString operator()(const ScriptEditorStatements::MatchedCondition &matched)
{ {
if (!matched.statements.size() && !matched.tokens.size()) if (!matched.statements.size() && !matched.tokens.size())
return {}; return {};
@@ -209,7 +209,7 @@ struct JSOverload
return value; return value;
} }
QString operator()(const ConnectionEditorStatements::MatchedStatement &statement) QString operator()(const ScriptEditorStatements::MatchedStatement &statement)
{ {
if (isEmptyStatement(statement)) if (isEmptyStatement(statement))
return {}; return {};
@@ -217,7 +217,7 @@ struct JSOverload
return std::visit(JSOverload{}, statement); return std::visit(JSOverload{}, statement);
} }
QString operator()(const ConnectionEditorStatements::ConditionalStatement &conditional) QString operator()(const ScriptEditorStatements::ConditionalStatement &conditional)
{ {
QString value; QString value;
value.reserve(200); value.reserve(200);
@@ -240,47 +240,47 @@ struct JSOverload
} // namespace } // namespace
bool ConnectionEditorStatements::isEmptyStatement(const MatchedStatement &stat) bool ScriptEditorStatements::isEmptyStatement(const MatchedStatement &stat)
{ {
return std::holds_alternative<EmptyBlock>(stat); return std::holds_alternative<EmptyBlock>(stat);
} }
QString ConnectionEditorStatements::toString(const ComparativeStatement &stat) QString ScriptEditorStatements::toString(const ComparativeStatement &stat)
{ {
return std::visit(StringVisitor{}, stat); return std::visit(StringVisitor{}, stat);
} }
QString ConnectionEditorStatements::toString(const RightHandSide &rhs) QString ScriptEditorStatements::toString(const RightHandSide &rhs)
{ {
return std::visit(StringVisitor{}, rhs); return std::visit(StringVisitor{}, rhs);
} }
QString ConnectionEditorStatements::toString(const Literal &literal) QString ScriptEditorStatements::toString(const Literal &literal)
{ {
return std::visit(StringVisitor{}, literal); return std::visit(StringVisitor{}, literal);
} }
QString ConnectionEditorStatements::toString(const MatchedStatement &statement) QString ScriptEditorStatements::toString(const MatchedStatement &statement)
{ {
return std::visit(StringVisitor{}, statement); return std::visit(StringVisitor{}, statement);
} }
QString ConnectionEditorStatements::toString(const Handler &handler) QString ScriptEditorStatements::toString(const Handler &handler)
{ {
return std::visit(StringVisitor{}, handler); return std::visit(StringVisitor{}, handler);
} }
QString ConnectionEditorStatements::toJavascript(const Handler &handler) QString ScriptEditorStatements::toJavascript(const Handler &handler)
{ {
return std::visit(JSOverload{}, handler); return std::visit(JSOverload{}, handler);
} }
bool ConnectionEditorStatements::isConsoleLog(const MatchedStatement &curState) bool ScriptEditorStatements::isConsoleLog(const MatchedStatement &curState)
{ {
return std::holds_alternative<ConsoleLog>(curState); return std::holds_alternative<ConsoleLog>(curState);
} }
bool ConnectionEditorStatements::isLiteralType(const RightHandSide &var) bool ScriptEditorStatements::isLiteralType(const RightHandSide &var)
{ {
return std::visit(Overload{[](const double &) { return true; }, return std::visit(Overload{[](const double &) { return true; },
[](const bool &) { return true; }, [](const bool &) { return true; },
@@ -289,7 +289,7 @@ bool ConnectionEditorStatements::isLiteralType(const RightHandSide &var)
var); var);
} }
QString ConnectionEditorStatements::toDisplayName(const MatchedStatement &statement) QString ScriptEditorStatements::toDisplayName(const MatchedStatement &statement)
{ {
const char *displayName = std::visit( const char *displayName = std::visit(
Overload{[](const MatchedFunction &) { return FUNCTION_DISPLAY_NAME; }, Overload{[](const MatchedFunction &) { return FUNCTION_DISPLAY_NAME; },
@@ -303,7 +303,7 @@ QString ConnectionEditorStatements::toDisplayName(const MatchedStatement &statem
return QString::fromLatin1(displayName); return QString::fromLatin1(displayName);
} }
QString ConnectionEditorStatements::toDisplayName(const Handler &handler) QString ScriptEditorStatements::toDisplayName(const Handler &handler)
{ {
const MatchedStatement &statement = std::visit( const MatchedStatement &statement = std::visit(
Overload{[](const MatchedStatement &statement) { return statement; }, Overload{[](const MatchedStatement &statement) { return statement; },
@@ -312,51 +312,49 @@ QString ConnectionEditorStatements::toDisplayName(const Handler &handler)
return toDisplayName(statement); return toDisplayName(statement);
} }
MatchedStatement &ConnectionEditorStatements::okStatement( MatchedStatement &ScriptEditorStatements::okStatement(ScriptEditorStatements::Handler &handler)
ConnectionEditorStatements::Handler &handler)
{ {
MatchedStatement statement; MatchedStatement statement;
return std::visit(Overload{[](ConnectionEditorStatements::MatchedStatement &var) return std::visit(Overload{[](ScriptEditorStatements::MatchedStatement &var) -> MatchedStatement & {
-> MatchedStatement & { return var; }, return var;
[](ConnectionEditorStatements::ConditionalStatement &statement) },
[](ScriptEditorStatements::ConditionalStatement &statement)
-> MatchedStatement & { return statement.ok; }}, -> MatchedStatement & { return statement.ok; }},
handler); handler);
} }
MatchedStatement &ConnectionEditorStatements::koStatement( MatchedStatement &ScriptEditorStatements::koStatement(ScriptEditorStatements::Handler &handler)
ConnectionEditorStatements::Handler &handler)
{ {
static MatchedStatement block; static MatchedStatement block;
if (auto *statement = std::get_if<ConnectionEditorStatements::ConditionalStatement>(&handler)) if (auto *statement = std::get_if<ScriptEditorStatements::ConditionalStatement>(&handler))
return statement->ko; return statement->ko;
return block; return block;
} }
MatchedCondition &ConnectionEditorStatements::matchedCondition(Handler &handler) MatchedCondition &ScriptEditorStatements::matchedCondition(Handler &handler)
{ {
static MatchedCondition block; static MatchedCondition block;
if (auto *statement = std::get_if<ConnectionEditorStatements::ConditionalStatement>(&handler)) if (auto *statement = std::get_if<ScriptEditorStatements::ConditionalStatement>(&handler))
return statement->condition; return statement->condition;
return block; return block;
} }
ConditionalStatement &ConnectionEditorStatements::conditionalStatement( ConditionalStatement &ScriptEditorStatements::conditionalStatement(ScriptEditorStatements::Handler &handler)
ConnectionEditorStatements::Handler &handler)
{ {
static ConditionalStatement block; static ConditionalStatement block;
if (auto *statement = std::get_if<ConnectionEditorStatements::ConditionalStatement>(&handler)) if (auto *statement = std::get_if<ScriptEditorStatements::ConditionalStatement>(&handler))
return *statement; return *statement;
return block; return block;
} }
QString ConnectionEditorStatements::toJavascript(const ConditionToken &token) QString ScriptEditorStatements::toJavascript(const ConditionToken &token)
{ {
switch (token) { switch (token) {
case ConditionToken::Not: case ConditionToken::Not:

View File

@@ -7,22 +7,22 @@
#include <qmljs/parser/qmljsast_p.h> #include <qmljs/parser/qmljsast_p.h>
namespace QmlDesigner { namespace QmlDesigner {
namespace ConnectionEditorStatements { namespace ScriptEditorStatements {
inline constexpr char FUNCTION_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( inline constexpr char FUNCTION_DISPLAY_NAME[] = QT_TRANSLATE_NOOP(
"QmlDesigner::ConnectionEditorStatements", "Function"); "QmlDesigner::ScriptEditorStatements", "Function");
inline constexpr char ASSIGNMENT_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( inline constexpr char ASSIGNMENT_DISPLAY_NAME[] = QT_TRANSLATE_NOOP(
"QmlDesigner::ConnectionEditorStatements", "Assignment"); "QmlDesigner::ScriptEditorStatements", "Assignment");
inline constexpr char SETPROPERTY_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( inline constexpr char SETPROPERTY_DISPLAY_NAME[] = QT_TRANSLATE_NOOP(
"QmlDesigner::ConnectionEditorStatements", "Set Property"); "QmlDesigner::ScriptEditorStatements", "Set Property");
inline constexpr char SETSTATE_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( inline constexpr char SETSTATE_DISPLAY_NAME[] = QT_TRANSLATE_NOOP(
"QmlDesigner::ConnectionEditorStatements", "Set State"); "QmlDesigner::ScriptEditorStatements", "Set State");
inline constexpr char LOG_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( inline constexpr char LOG_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QmlDesigner::ScriptEditorStatements",
"QmlDesigner::ConnectionEditorStatements", "Print"); "Print");
inline constexpr char EMPTY_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( inline constexpr char EMPTY_DISPLAY_NAME[] = QT_TRANSLATE_NOOP(
"QmlDesigner::ConnectionEditorStatements", "Empty"); "QmlDesigner::ScriptEditorStatements", "Empty");
inline constexpr char CUSTOM_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( inline constexpr char CUSTOM_DISPLAY_NAME[] = QT_TRANSLATE_NOOP(
"QmlDesigner::ConnectionEditorStatements", "Custom"); "QmlDesigner::ScriptEditorStatements", "Custom");
struct Variable; struct Variable;
struct MatchedFunction; struct MatchedFunction;
@@ -122,13 +122,12 @@ QMLDESIGNER_EXPORT QString toDisplayName(const MatchedStatement &statement);
QMLDESIGNER_EXPORT QString toDisplayName(const Handler &handler); QMLDESIGNER_EXPORT QString toDisplayName(const Handler &handler);
QMLDESIGNER_EXPORT QString toJavascript(const ConditionToken &token); QMLDESIGNER_EXPORT QString toJavascript(const ConditionToken &token);
QMLDESIGNER_EXPORT MatchedStatement &okStatement(ConnectionEditorStatements::Handler &handler); QMLDESIGNER_EXPORT MatchedStatement &okStatement(ScriptEditorStatements::Handler &handler);
QMLDESIGNER_EXPORT MatchedStatement &koStatement(ConnectionEditorStatements::Handler &handler); QMLDESIGNER_EXPORT MatchedStatement &koStatement(ScriptEditorStatements::Handler &handler);
QMLDESIGNER_EXPORT MatchedCondition &matchedCondition(ConnectionEditorStatements::Handler &handler); QMLDESIGNER_EXPORT MatchedCondition &matchedCondition(ScriptEditorStatements::Handler &handler);
QMLDESIGNER_EXPORT ConditionalStatement &conditionalStatement( QMLDESIGNER_EXPORT ConditionalStatement &conditionalStatement(ScriptEditorStatements::Handler &handler);
ConnectionEditorStatements::Handler &handler);
} // namespace ConnectionEditorStatements } // namespace ScriptEditorStatements
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -1,6 +1,6 @@
// Copyright (C) 2023 The Qt Company Ltd. // Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "connectioneditorutils.h" #include "scripteditorutils.h"
#include <abstractproperty.h> #include <abstractproperty.h>
#include <abstractview.h> #include <abstractview.h>
@@ -8,11 +8,11 @@
#include <modelnode.h> #include <modelnode.h>
#include <nodeabstractproperty.h> #include <nodeabstractproperty.h>
#include <nodemetainfo.h> #include <nodemetainfo.h>
#include <qmldesignertr.h>
#include <rewriterview.h> #include <rewriterview.h>
#include <rewritingexception.h> #include <rewritingexception.h>
#include <type_traits> #include <type_traits>
#include <variantproperty.h> #include <variantproperty.h>
#include <qmldesignertr.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -24,7 +24,7 @@
namespace QmlDesigner { namespace QmlDesigner {
Q_LOGGING_CATEGORY(ConnectionEditorLog, "qtc.qtquickdesigner.connectioneditor", QtWarningMsg) Q_LOGGING_CATEGORY(ScriptEditorLog, "qtc.qtquickdesigner.scripteditor", QtWarningMsg)
void callLater(const std::function<void()> &fun) void callLater(const std::function<void()> &fun)
{ {
@@ -62,7 +62,7 @@ PropertyName uniquePropertyName(const PropertyName &suggestion, const ModelNode
NodeMetaInfo dynamicTypeNameToNodeMetaInfo(const TypeName &typeName, Model *model) NodeMetaInfo dynamicTypeNameToNodeMetaInfo(const TypeName &typeName, Model *model)
{ {
// Note: Uses old mechanism to create the NodeMetaInfo and supports // Note: Uses old mechanism to create the NodeMetaInfo and supports
// only types we care about in the connection editor. // only types we care about in the script editor.
// TODO: Support all possible AbstractProperty types and move to the // TODO: Support all possible AbstractProperty types and move to the
// AbstractProperty class. // AbstractProperty class.
if (typeName == "bool") if (typeName == "bool")
@@ -80,7 +80,7 @@ NodeMetaInfo dynamicTypeNameToNodeMetaInfo(const TypeName &typeName, Model *mode
else if (typeName == "var" || typeName == "variant") else if (typeName == "var" || typeName == "variant")
return model->metaInfo("QML.variant"); return model->metaInfo("QML.variant");
else else
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "type" << typeName << "not found"; qCWarning(ScriptEditorLog) << __FUNCTION__ << "type" << typeName << "not found";
return {}; return {};
} }
@@ -355,7 +355,7 @@ QStringList availableTargetProperties(const BindingProperty &bindingProperty)
{ {
const ModelNode modelNode = bindingProperty.parentModelNode(); const ModelNode modelNode = bindingProperty.parentModelNode();
if (!modelNode.isValid()) { if (!modelNode.isValid()) {
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "invalid model node"; qCWarning(ScriptEditorLog) << __FUNCTION__ << "invalid model node";
return {}; return {};
} }
@@ -428,7 +428,7 @@ QStringList availableSourceProperties(const QString &id,
} else if (auto metaInfo = targetProperty.parentModelNode().metaInfo(); metaInfo.isValid()) { } else if (auto metaInfo = targetProperty.parentModelNode().metaInfo(); metaInfo.isValid()) {
targetType = metaInfo.property(targetProperty.name()).propertyType(); targetType = metaInfo.property(targetProperty.name()).propertyType();
} else } else
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "no meta info for target node"; qCWarning(ScriptEditorLog) << __FUNCTION__ << "no meta info for target node";
QStringList possibleProperties; QStringList possibleProperties;
if (!modelNode.isValid()) { if (!modelNode.isValid()) {
@@ -450,7 +450,7 @@ QStringList availableSourceProperties(const QString &id,
return possibleProperties; return possibleProperties;
} }
#endif #endif
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "invalid model node:" << id; qCWarning(ScriptEditorLog) << __FUNCTION__ << "invalid model node:" << id;
return {}; return {};
} }
@@ -476,10 +476,40 @@ QStringList availableSourceProperties(const QString &id,
possibleProperties.push_back(QString::fromUtf8(property.name())); possibleProperties.push_back(QString::fromUtf8(property.name()));
} }
} else { } else {
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "no meta info for source node"; qCWarning(ScriptEditorLog) << __FUNCTION__ << "no meta info for source node";
} }
return possibleProperties; return possibleProperties;
} }
QString addOnToSignalName(const QString &signal)
{
if (signal.isEmpty())
return {};
static const QRegularExpression rx("^on[A-Z]");
if (rx.match(signal).hasMatch())
return signal;
QString ret = signal;
ret[0] = ret.at(0).toUpper();
ret.prepend("on");
return ret;
}
QString removeOnFromSignalName(const QString &signal)
{
if (signal.isEmpty())
return {};
static const QRegularExpression rx("^on[A-Z]");
if (!rx.match(signal).hasMatch())
return signal;
QString ret = signal;
ret.remove(0, 2);
ret[0] = ret.at(0).toLower();
return ret;
}
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -13,7 +13,7 @@
namespace QmlDesigner { namespace QmlDesigner {
Q_DECLARE_LOGGING_CATEGORY(ConnectionEditorLog) Q_DECLARE_LOGGING_CATEGORY(ScriptEditorLog)
class AbstractView; class AbstractView;
class AbstractProperty; class AbstractProperty;
@@ -26,6 +26,8 @@ void showErrorMessage(const QString &text);
QString idOrTypeName(const ModelNode &modelNode); QString idOrTypeName(const ModelNode &modelNode);
PropertyName uniquePropertyName(const PropertyName &suggestion, const ModelNode &modelNode); PropertyName uniquePropertyName(const PropertyName &suggestion, const ModelNode &modelNode);
QString addOnToSignalName(const QString &signal);
QString removeOnFromSignalName(const QString &signal);
NodeMetaInfo dynamicTypeMetaInfo(const AbstractProperty &property); NodeMetaInfo dynamicTypeMetaInfo(const AbstractProperty &property);
NodeMetaInfo dynamicTypeNameToNodeMetaInfo(const TypeName &typeName, Model *model); NodeMetaInfo dynamicTypeNameToNodeMetaInfo(const TypeName &typeName, Model *model);