diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 5ca4ed28d25..5e40aaa446c 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -6,6 +6,7 @@ import QtQuick.Controls import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme as StudioTheme +import ScriptEditorBackend Column { id: root @@ -71,32 +72,32 @@ Column { model: ListModel { ListElement { - value: ConnectionModelStatementDelegate.CallFunction + value: StatementDelegate.CallFunction text: qsTr("Call Function") enabled: true } ListElement { - value: ConnectionModelStatementDelegate.Assign + value: StatementDelegate.Assign text: qsTr("Assign") enabled: true } ListElement { - value: ConnectionModelStatementDelegate.ChangeState + value: StatementDelegate.ChangeState text: qsTr("Change State") enabled: true } ListElement { - value: ConnectionModelStatementDelegate.SetProperty + value: StatementDelegate.SetProperty text: qsTr("Set Property") enabled: true } ListElement { - value: ConnectionModelStatementDelegate.PrintMessage + value: StatementDelegate.PrintMessage text: qsTr("Print Message") enabled: true } ListElement { - value: ConnectionModelStatementDelegate.Custom + value: StatementDelegate.Custom text: qsTr("Custom") enabled: false } @@ -106,7 +107,7 @@ Column { StatementEditor { width: root.width - actionType: action.currentValue ?? ConnectionModelStatementDelegate.Custom + actionType: action.currentValue ?? StatementDelegate.Custom horizontalSpacing: root.horizontalSpacing columnWidth: root.columnWidth statement: backend.okStatement @@ -122,7 +123,7 @@ Column { iconSize: StudioTheme.Values.baseFontSize iconFontFamily: StudioTheme.Constants.font.family anchors.horizontalCenter: parent.horizontalCenter - visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && !backend.hasCondition + visible: action.currentValue !== StatementDelegate.Custom && !backend.hasCondition onClicked: backend.addCondition() } @@ -135,7 +136,7 @@ Column { iconSize: StudioTheme.Values.baseFontSize iconFontFamily: StudioTheme.Constants.font.family anchors.horizontalCenter: parent.horizontalCenter - visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && backend.hasCondition + visible: action.currentValue !== StatementDelegate.Custom && backend.hasCondition onClicked: backend.removeCondition() } @@ -186,7 +187,7 @@ Column { iconSize: StudioTheme.Values.baseFontSize iconFontFamily: StudioTheme.Constants.font.family anchors.horizontalCenter: parent.horizontalCenter - visible: action.currentValue !== ConnectionModelStatementDelegate.Custom + visible: action.currentValue !== StatementDelegate.Custom && backend.hasCondition && !backend.hasElse onClicked: backend.addElse() @@ -200,7 +201,7 @@ Column { iconSize: StudioTheme.Values.baseFontSize iconFontFamily: StudioTheme.Constants.font.family anchors.horizontalCenter: parent.horizontalCenter - visible: action.currentValue !== ConnectionModelStatementDelegate.Custom + visible: action.currentValue !== StatementDelegate.Custom && backend.hasCondition && backend.hasElse onClicked: backend.removeElse() @@ -209,13 +210,13 @@ Column { //Else Statement StatementEditor { width: root.width - actionType: action.currentValue ?? ConnectionModelStatementDelegate.Custom + actionType: action.currentValue ?? StatementDelegate.Custom horizontalSpacing: root.horizontalSpacing columnWidth: root.columnWidth statement: backend.koStatement backend: root.backend spacing: root.verticalSpacing - visible: action.currentValue !== ConnectionModelStatementDelegate.Custom + visible: action.currentValue !== StatementDelegate.Custom && backend.hasCondition && backend.hasElse } diff --git a/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml index f198d84a103..0783f3cb7a0 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml @@ -7,6 +7,7 @@ import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme as StudioTheme import ConnectionsEditorEditorBackend +import ScriptEditorBackend Column { id: root @@ -22,7 +23,7 @@ Column { // Call Function Row { - visible: root.actionType === ConnectionModelStatementDelegate.CallFunction + visible: root.actionType === StatementDelegate.CallFunction spacing: root.horizontalSpacing PopupLabel { @@ -39,7 +40,7 @@ Column { } Row { - visible: root.actionType === ConnectionModelStatementDelegate.CallFunction + visible: root.actionType === StatementDelegate.CallFunction spacing: root.horizontalSpacing StudioControls.TopLevelComboBox { @@ -68,7 +69,7 @@ Column { // Assign Row { - visible: root.actionType === ConnectionModelStatementDelegate.Assign + visible: root.actionType === StatementDelegate.Assign spacing: root.horizontalSpacing PopupLabel { @@ -84,7 +85,7 @@ Column { } Row { - visible: root.actionType === ConnectionModelStatementDelegate.Assign + visible: root.actionType === StatementDelegate.Assign spacing: root.horizontalSpacing StudioControls.TopLevelComboBox { @@ -114,7 +115,7 @@ Column { } Row { - visible: root.actionType === ConnectionModelStatementDelegate.Assign + visible: root.actionType === StatementDelegate.Assign spacing: root.horizontalSpacing StudioControls.TopLevelComboBox { @@ -145,7 +146,7 @@ Column { // Change State Row { - visible: root.actionType === ConnectionModelStatementDelegate.ChangeState + visible: root.actionType === StatementDelegate.ChangeState spacing: root.horizontalSpacing PopupLabel { @@ -162,7 +163,7 @@ Column { } Row { - visible: root.actionType === ConnectionModelStatementDelegate.ChangeState + visible: root.actionType === StatementDelegate.ChangeState spacing: root.horizontalSpacing StudioControls.TopLevelComboBox { @@ -191,7 +192,7 @@ Column { // Set Property Row { - visible: root.actionType === ConnectionModelStatementDelegate.SetProperty + visible: root.actionType === StatementDelegate.SetProperty spacing: root.horizontalSpacing PopupLabel { @@ -208,7 +209,7 @@ Column { } Row { - visible: root.actionType === ConnectionModelStatementDelegate.SetProperty + visible: root.actionType === StatementDelegate.SetProperty spacing: root.horizontalSpacing StudioControls.TopLevelComboBox { @@ -238,14 +239,14 @@ Column { PopupLabel { width: root.columnWidth - visible: root.actionType === ConnectionModelStatementDelegate.SetProperty + visible: root.actionType === StatementDelegate.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 { id: setPropertyArgument - visible: root.actionType === ConnectionModelStatementDelegate.SetProperty + visible: root.actionType === StatementDelegate.SetProperty width: root.width actionIndicatorVisible: false translationIndicatorVisible: false @@ -259,14 +260,14 @@ Column { // Print Message PopupLabel { width: root.columnWidth - visible: root.actionType === ConnectionModelStatementDelegate.PrintMessage + visible: root.actionType === StatementDelegate.PrintMessage text: qsTr("Message") tooltip: qsTr("Sets a text that is printed when the Signal of the Target component initiates.") } StudioControls.TextField { id: messageString - visible: root.actionType === ConnectionModelStatementDelegate.PrintMessage + visible: root.actionType === StatementDelegate.PrintMessage width: root.width actionIndicatorVisible: false translationIndicatorVisible: false @@ -278,7 +279,7 @@ Column { // Custom PopupLabel { - visible: root.actionType === ConnectionModelStatementDelegate.Custom + visible: root.actionType === StatementDelegate.Custom text: qsTr("Custom Connections can only be edited with the binding editor") width: root.width horizontalAlignment: Text.AlignHCenter diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 0ba34894e2a..1c58e2ba88f 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -571,6 +571,16 @@ extend_qtc_plugin(QmlDesigner 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 SOURCES_PREFIX components/connectioneditor SOURCES @@ -578,15 +588,12 @@ extend_qtc_plugin(QmlDesigner bindingmodel.cpp bindingmodel.h bindingmodelitem.cpp bindingmodelitem.h connectioneditor.qrc - connectioneditorevaluator.cpp connectioneditorevaluator.h - connectioneditorstatements.cpp connectioneditorstatements.h connectionmodel.cpp connectionmodel.h connectionview.cpp connectionview.h dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h dynamicpropertiesitem.cpp dynamicpropertiesitem.h - connectioneditorutils.cpp connectioneditorutils.h selectiondynamicpropertiesproxymodel.cpp selectiondynamicpropertiesproxymodel.h - propertytreemodel.cpp propertytreemodel.h + connectioneditorlogging.cpp connectioneditorlogging.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index 4ef8a4ed378..4b4e59cfe00 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -3,10 +3,11 @@ #include "bindingmodel.h" #include "bindingmodelitem.h" -#include "connectioneditorutils.h" +#include "connectioneditorlogging.h" #include "connectionview.h" #include "modelfwd.h" +#include #include #include #include diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.cpp index e5e1372dd65..a0976564e90 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "bindingmodelitem.h" -#include "connectioneditorutils.h" +#include #include #include diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorlogging.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorlogging.cpp new file mode 100644 index 00000000000..0ed91b33ab3 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorlogging.cpp @@ -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 diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorlogging.h b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorlogging.h new file mode 100644 index 00000000000..673a5b1a32f --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorlogging.h @@ -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 + +namespace QmlDesigner { + +Q_DECLARE_LOGGING_CATEGORY(ConnectionEditorLog) + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index f4462f911f5..c0a13a788e8 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -2,41 +2,22 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "connectionmodel.h" -#include "connectioneditorutils.h" +#include "connectioneditorlogging.h" #include "connectionview.h" -#include "utils/algorithm.h" -#include -#include -#include -#include -#include #include -#include #include -#include #include #include #include #include -#include +#include +#include #include -#include - -#include #include -#include -#include -#include -#include -#include -#include namespace { - -const char defaultCondition[] = "condition"; - QStringList propertyNameListToStringList(const QmlDesigner::PropertyNameList &propertyNameList) { QStringList stringList; @@ -308,8 +289,7 @@ void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerP //anything else is assignment // e.g. foo.bal = foo2.bula ; foo.bal = "literal" ; goo.gal = true - item->setData(tr(ConnectionEditorEvaluator::getDisplayStringForType( - signalHandlerProperty.source()) + item->setData(tr(ScriptEditorEvaluator::getDisplayStringForType(signalHandlerProperty.source()) .toLatin1()), UserRoles::ActionTypeRole); } @@ -344,21 +324,6 @@ ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connectio return result; } -static 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; -} - static PropertyName getFirstSignalForTarget(const NodeMetaInfo &target) { PropertyName ret = "clicked"; @@ -738,208 +703,22 @@ QHash ConnectionModel::roleNames() const return roleNames; } -ConnectionModelBackendDelegate::ConnectionModelBackendDelegate(ConnectionModel *model) - : m_signalDelegate(model->connectionView()) - , m_okStatementDelegate(model) - , m_koStatementDelegate(model) - , m_conditionListModel(model) - , m_propertyTreeModel(model->connectionView()) - , m_propertyListProxyModel(&m_propertyTreeModel) - , m_model(model) -{ - connect(&m_signalDelegate, &PropertyTreeModelDelegate::commitData, this, [this] { - handleTargetChanged(); - }); - - connect(&m_okStatementDelegate, - &ConnectionModelStatementDelegate::statementChanged, - this, - [this] { handleOkStatementChanged(); }); - - connect(&m_koStatementDelegate, - &ConnectionModelStatementDelegate::statementChanged, - this, - [this] { handleKOStatementChanged(); }); - - connect(&m_conditionListModel, &ConditionListModel::conditionChanged, this, [this] { - handleConditionChanged(); - }); - - m_signalDelegate.setPropertyType(PropertyTreeModel::SignalType); -} - -QString generateDefaultStatement(ConnectionModelBackendDelegate::ActionType actionType, - const QString &rootId) -{ - switch (actionType) { - case ConnectionModelStatementDelegate::CallFunction: - return "Qt.quit()"; - case ConnectionModelStatementDelegate::Assign: - return QString("%1.visible = %1.visible").arg(rootId); - case ConnectionModelStatementDelegate::ChangeState: - return QString("%1.state = \"\"").arg(rootId); - case ConnectionModelStatementDelegate::SetProperty: - return QString("%1.visible = true").arg(rootId); - case ConnectionModelStatementDelegate::PrintMessage: - return QString("console.log(\"test\")").arg(rootId); - case ConnectionModelStatementDelegate::Custom: - return {}; - }; - - //Qt.quit() - //console.log("test") - //root.state = "" - //root.visible = root.visible - //root.visible = true - - return {}; -} - -void ConnectionModelBackendDelegate::changeActionType(ActionType actionType) -{ - QTC_ASSERT(actionType != ConnectionModelStatementDelegate::Custom, return ); - - ConnectionModel *model = m_model; - - QTC_ASSERT(model, return ); - QTC_ASSERT(model->connectionView()->isAttached(), return ); - - const QString validId = model->connectionView()->rootModelNode().validId(); - - SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); - - // Do not take ko into account for now - - model->connectionView() - ->executeInTransaction("ConnectionModelBackendDelegate::removeCondition", [&]() { - 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); - - auto tempHandler = ConnectionEditorEvaluator::parseStatement(statementSource); - - auto newOkStatement = ConnectionEditorStatements::okStatement(tempHandler); - - QTC_ASSERT(!ConnectionEditorStatements::isEmptyStatement(newOkStatement), return ); - - okStatement = newOkStatement; - - QString newSource = ConnectionEditorStatements::toJavascript(m_handler); - - signalHandlerProperty.setSource(newSource); - }); - - setSource(signalHandlerProperty.source()); - - setupHandlerAndStatements(); - setupCondition(); -} - -void ConnectionModelBackendDelegate::addCondition() -{ - ConnectionEditorStatements::MatchedStatement okStatement - = ConnectionEditorStatements::okStatement(m_handler); - - ConnectionEditorStatements::MatchedCondition newCondition; - - ConnectionEditorStatements::Variable variable; - variable.nodeId = defaultCondition; - newCondition.statements.append(variable); - - ConnectionEditorStatements::ConditionalStatement conditionalStatement; - - conditionalStatement.ok = okStatement; - conditionalStatement.condition = newCondition; - - m_handler = conditionalStatement; - - QString newSource = ConnectionEditorStatements::toJavascript(m_handler); - - commitNewSource(newSource); - - setupHandlerAndStatements(); - setupCondition(); -} - -void ConnectionModelBackendDelegate::removeCondition() -{ - ConnectionEditorStatements::MatchedStatement okStatement - = ConnectionEditorStatements::okStatement(m_handler); - - m_handler = okStatement; - - QString newSource = ConnectionEditorStatements::toJavascript(m_handler); - - commitNewSource(newSource); - - setupHandlerAndStatements(); - setupCondition(); -} - -void ConnectionModelBackendDelegate::addElse() -{ - ConnectionEditorStatements::MatchedStatement okStatement - = ConnectionEditorStatements::okStatement(m_handler); - - auto &condition = ConnectionEditorStatements::conditionalStatement(m_handler); - condition.ko = condition.ok; - - QString newSource = ConnectionEditorStatements::toJavascript(m_handler); - - - commitNewSource(newSource); - setupHandlerAndStatements(); -} - -void ConnectionModelBackendDelegate::removeElse() -{ - ConnectionEditorStatements::MatchedStatement okStatement - = ConnectionEditorStatements::okStatement(m_handler); - - auto &condition = ConnectionEditorStatements::conditionalStatement(m_handler); - condition.ko = ConnectionEditorStatements::EmptyBlock(); - - QString newSource = ConnectionEditorStatements::toJavascript(m_handler); - - - commitNewSource(newSource); - setupHandlerAndStatements(); -} - -void ConnectionModelBackendDelegate::setNewSource(const QString &newSource) -{ - setSource(newSource); - commitNewSource(newSource); - setupHandlerAndStatements(); - setupCondition(); -} - int ConnectionModelBackendDelegate::currentRow() const { return m_currentRow; } -static QString removeOnFromSignalName(const QString &signal) +ConnectionModelBackendDelegate::ConnectionModelBackendDelegate(ConnectionModel *model) + : ScriptEditorBackend(model->connectionView()) + , m_signalDelegate(model->connectionView()) + , m_model(model) { - if (signal.isEmpty()) - return {}; + connect(&m_signalDelegate, + &PropertyTreeModelDelegate::commitData, + this, + &ConnectionModelBackendDelegate::handleTargetChanged); - 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; + m_signalDelegate.setPropertyType(PropertyTreeModel::SignalType); } void ConnectionModelBackendDelegate::setCurrentRow(int i) @@ -954,22 +733,22 @@ void ConnectionModelBackendDelegate::setCurrentRow(int i) void ConnectionModelBackendDelegate::update() { - if (m_blockReflection) - return; - if (m_currentRow == -1) return; - m_propertyTreeModel.resetModel(); - m_propertyListProxyModel.setRowAndInternalId(0, internalRootIndex); + if (blockReflection()) { + return; + } + + ScriptEditorBackend::update(); ConnectionModel *model = m_model; - QTC_ASSERT(model, return ); + QTC_ASSERT(model, return); if (!model->connectionView()->isAttached()) return; - SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); + auto signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); QStringList targetNodes; @@ -988,65 +767,8 @@ void ConnectionModelBackendDelegate::update() if (!targetNodes.contains(targetNodeName)) targetNodes.append(targetNodeName); - setSource(signalHandlerProperty.source()); - m_signalDelegate.setup(targetNodeName, removeOnFromSignalName(QString::fromUtf8(signalHandlerProperty.name()))); - - setupHandlerAndStatements(); - - setupCondition(); - - QTC_ASSERT(model, return ); -} - -void ConnectionModelBackendDelegate::jumpToCode() -{ - ConnectionModel *model = m_model; - - QTC_ASSERT(model, return ); - QTC_ASSERT(model->connectionView()->isAttached(), return ); - SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); - - ModelNodeOperations::jumpToCode(signalHandlerProperty.parentModelNode()); -} - -void ConnectionModelBackendDelegate::handleException() -{ - QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); -} - -bool ConnectionModelBackendDelegate::hasCondition() const -{ - return m_hasCondition; -} - -bool ConnectionModelBackendDelegate::hasElse() const -{ - return m_hasElse; -} - -void ConnectionModelBackendDelegate::setHasCondition(bool b) -{ - if (b == m_hasCondition) - return; - - m_hasCondition = b; - emit hasConditionChanged(); -} - -void ConnectionModelBackendDelegate::setHasElse(bool b) -{ - if (b == m_hasElse) - return; - - m_hasElse = b; - emit hasElseChanged(); -} - -ConnectionModelBackendDelegate::ActionType ConnectionModelBackendDelegate::actionType() const -{ - return m_actionType; } PropertyTreeModelDelegate *ConnectionModelBackendDelegate::signal() @@ -1054,135 +776,21 @@ PropertyTreeModelDelegate *ConnectionModelBackendDelegate::signal() return &m_signalDelegate; } -ConnectionModelStatementDelegate *ConnectionModelBackendDelegate::okStatement() -{ - return &m_okStatementDelegate; -} - -ConnectionModelStatementDelegate *ConnectionModelBackendDelegate::koStatement() -{ - return &m_koStatementDelegate; -} - -ConditionListModel *ConnectionModelBackendDelegate::conditionListModel() -{ - return &m_conditionListModel; -} - -QString ConnectionModelBackendDelegate::indentedSource() const -{ - if (m_source.isEmpty()) - return {}; - - QTextDocument doc(m_source); - IndentingTextEditModifier mod(&doc); - - mod.indent(0, m_source.length() - 1); - return mod.text(); -} - -QString ConnectionModelBackendDelegate::source() const -{ - return m_source; -} - -void ConnectionModelBackendDelegate::setSource(const QString &source) -{ - if (source == m_source) - return; - - m_source = source; - emit sourceChanged(); -} - -PropertyTreeModel *ConnectionModelBackendDelegate::propertyTreeModel() -{ - return &m_propertyTreeModel; -} - -PropertyListProxyModel *ConnectionModelBackendDelegate::propertyListProxyModel() -{ - return &m_propertyListProxyModel; -} - -void ConnectionModelBackendDelegate::setupCondition() -{ - auto &condition = ConnectionEditorStatements::matchedCondition(m_handler); - m_conditionListModel.setCondition(ConnectionEditorStatements::matchedCondition(m_handler)); - setHasCondition(!condition.statements.isEmpty()); -} - -void ConnectionModelBackendDelegate::setupHandlerAndStatements() -{ - ConnectionModel *model = m_model; - QTC_ASSERT(model, return ); - SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); - - if (signalHandlerProperty.source().isEmpty()) { - m_actionType = ConnectionModelStatementDelegate::Custom; - m_handler = ConnectionEditorStatements::EmptyBlock(); - } else { - m_handler = ConnectionEditorEvaluator::parseStatement(signalHandlerProperty.source()); - - const QString statementType = QmlDesigner::ConnectionEditorStatements::toDisplayName( - m_handler); - - if (statementType == ConnectionEditorStatements::EMPTY_DISPLAY_NAME) { - m_actionType = ConnectionModelStatementDelegate::Custom; - } else if (statementType == ConnectionEditorStatements::ASSIGNMENT_DISPLAY_NAME) { - m_actionType = ConnectionModelStatementDelegate::Assign; - //setupAssignment(); - } else if (statementType == ConnectionEditorStatements::SETPROPERTY_DISPLAY_NAME) { - m_actionType = ConnectionModelStatementDelegate::SetProperty; - //setupSetProperty(); - } else if (statementType == ConnectionEditorStatements::FUNCTION_DISPLAY_NAME) { - m_actionType = ConnectionModelStatementDelegate::CallFunction; - //setupCallFunction(); - } else if (statementType == ConnectionEditorStatements::SETSTATE_DISPLAY_NAME) { - m_actionType = ConnectionModelStatementDelegate::ChangeState; - //setupChangeState(); - } else if (statementType == ConnectionEditorStatements::LOG_DISPLAY_NAME) { - m_actionType = ConnectionModelStatementDelegate::PrintMessage; - //setupPrintMessage(); - } else { - m_actionType = ConnectionModelStatementDelegate::Custom; - } - } - - ConnectionEditorStatements::MatchedStatement &okStatement - = ConnectionEditorStatements::okStatement(m_handler); - m_okStatementDelegate.setStatement(okStatement); - m_okStatementDelegate.setActionType(m_actionType); - - ConnectionEditorStatements::MatchedStatement &koStatement - = ConnectionEditorStatements::koStatement(m_handler); - - if (!ConnectionEditorStatements::isEmptyStatement(koStatement)) { - m_koStatementDelegate.setStatement(koStatement); - m_koStatementDelegate.setActionType(m_actionType); - } - - ConnectionEditorStatements::isEmptyStatement(koStatement); - setHasElse(!ConnectionEditorStatements::isEmptyStatement(koStatement)); - - emit actionTypeChanged(); -} - void ConnectionModelBackendDelegate::handleTargetChanged() { ConnectionModel *model = m_model; - QTC_ASSERT(model, return ); + QTC_ASSERT(model, return); - QTC_ASSERT(model->connectionView()->isAttached(), return ); + QTC_ASSERT(model->connectionView()->isAttached(), return); - SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); + SignalHandlerProperty signalHandlerProperty = getSignalHandlerProperty(); const PropertyName handlerName = addOnToSignalName(m_signalDelegate.name()).toUtf8(); const auto parentModelNode = signalHandlerProperty.parentModelNode(); - QTC_ASSERT(parentModelNode.isValid(), return ); + QTC_ASSERT(parentModelNode.isValid(), return); const auto newId = m_signalDelegate.id(); @@ -1190,8 +798,9 @@ void ConnectionModelBackendDelegate::handleTargetChanged() model->connectionView() ->executeInTransaction("ConnectionModelBackendDelegate::handleTargetChanged", [&]() { - const auto oldTargetNodeName - = parentModelNode.bindingProperty("target").resolveToModelNode().id(); + const auto oldTargetNodeName = parentModelNode.bindingProperty("target") + .resolveToModelNode() + .id(); if (signalHandlerProperty.name() != handlerName) { const auto expression = signalHandlerProperty.source(); @@ -1212,996 +821,38 @@ void ConnectionModelBackendDelegate::handleTargetChanged() } }); - model->selectProperty(model->connectionView() - ->modelNodeForInternalId(internalId) - .signalHandlerProperty(handlerName)); + model->selectProperty( + model->connectionView()->modelNodeForInternalId(internalId).signalHandlerProperty(handlerName)); } -void ConnectionModelBackendDelegate::handleOkStatementChanged() +AbstractProperty ConnectionModelBackendDelegate::getSourceProperty() const { - ConnectionEditorStatements::MatchedStatement &okStatement - = ConnectionEditorStatements::okStatement(m_handler); - - okStatement = m_okStatementDelegate.statement(); //TODO why? - - QString newSource = ConnectionEditorStatements::toJavascript(m_handler); - - commitNewSource(newSource); + return getSignalHandlerProperty(); } -void ConnectionModelBackendDelegate::handleKOStatementChanged() +void ConnectionModelBackendDelegate::setPropertySource(const QString &source) { - ConnectionEditorStatements::MatchedStatement &koStatement - = ConnectionEditorStatements::koStatement(m_handler); + auto property = getSourceProperty(); - koStatement = m_koStatementDelegate.statement(); //TODO why? + QTC_ASSERT(property.isValid(), return); - QString newSource = ConnectionEditorStatements::toJavascript(m_handler); - - commitNewSource(newSource); -} - -void ConnectionModelBackendDelegate::handleConditionChanged() -{ - ConnectionModel *model = m_model; - - QTC_ASSERT(model, return ); - QTC_ASSERT(model->connectionView()->isAttached(), return ); - - ConnectionEditorStatements::MatchedCondition &condition - = ConnectionEditorStatements::matchedCondition(m_handler); - condition = m_conditionListModel.condition(); //why? - QString newSource = ConnectionEditorStatements::toJavascript(m_handler); - - commitNewSource(newSource); -} - -void ConnectionModelBackendDelegate::commitNewSource(const QString &source) -{ - ConnectionModel *model = m_model; - - QTC_ASSERT(model, return ); - - QTC_ASSERT(model->connectionView()->isAttached(), return ); - - SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); - - m_blockReflection = true; - model->connectionView()->executeInTransaction("ConnectionModelBackendDelegate::commitNewSource", - [&]() { - signalHandlerProperty.setSource(source); - }); - - setSource(signalHandlerProperty.source()); - m_blockReflection = false; -} - -static ConnectionEditorStatements::MatchedStatement emptyStatement; - -ConnectionModelStatementDelegate::ConnectionModelStatementDelegate(ConnectionModel *model) - : m_functionDelegate(model->connectionView()) - , m_lhsDelegate(model->connectionView()) - , m_rhsAssignmentDelegate(model->connectionView()) - , m_statement(emptyStatement) - , m_model(model) -{ - m_functionDelegate.setPropertyType(PropertyTreeModel::SlotType); - - connect(&m_functionDelegate, &PropertyTreeModelDelegate::commitData, this, [this] { - handleFunctionChanged(); - }); - - connect(&m_rhsAssignmentDelegate, &PropertyTreeModelDelegate::commitData, this, [this] { - handleRhsAssignmentChanged(); - }); - - connect(&m_lhsDelegate, &PropertyTreeModelDelegate::commitData, this, [this] { - handleLhsChanged(); - }); - - connect(&m_stringArgument, &StudioQmlTextBackend::activated, this, [this] { - handleStringArgumentChanged(); - }); - - connect(&m_states, &StudioQmlComboBoxBackend::activated, this, [this] { - handleStateChanged(); - }); - - connect(&m_stateTargets, &StudioQmlComboBoxBackend::activated, this, [this] { - handleStateTargetsChanged(); - }); -} - -void ConnectionModelStatementDelegate::setActionType(ActionType type) -{ - if (m_actionType == type) + if (source.isEmpty()) return; - m_actionType = type; - emit actionTypeChanged(); - setup(); -} - -void ConnectionModelStatementDelegate::setup() -{ - switch (m_actionType) { - case CallFunction: - setupCallFunction(); - break; - case Assign: - setupAssignment(); - break; - case ChangeState: - setupChangeState(); - break; - case SetProperty: - setupSetProperty(); - break; - case PrintMessage: - setupPrintMessage(); - break; - case Custom: - break; - }; -} - -void ConnectionModelStatementDelegate::setStatement( - ConnectionEditorStatements::MatchedStatement &statement) -{ - m_statement = statement; - setup(); -} - -ConnectionEditorStatements::MatchedStatement &ConnectionModelStatementDelegate::statement() -{ - return m_statement; -} - -ConnectionModelStatementDelegate::ActionType ConnectionModelStatementDelegate::actionType() const -{ - return m_actionType; -} - -PropertyTreeModelDelegate *ConnectionModelStatementDelegate::function() -{ - return &m_functionDelegate; -} - -PropertyTreeModelDelegate *ConnectionModelStatementDelegate::lhs() -{ - return &m_lhsDelegate; -} - -PropertyTreeModelDelegate *ConnectionModelStatementDelegate::rhsAssignment() -{ - return &m_rhsAssignmentDelegate; -} - -StudioQmlTextBackend *ConnectionModelStatementDelegate::stringArgument() -{ - return &m_stringArgument; -} - -StudioQmlComboBoxBackend *ConnectionModelStatementDelegate::stateTargets() -{ - return &m_stateTargets; -} - -StudioQmlComboBoxBackend *ConnectionModelStatementDelegate::states() -{ - return &m_states; -} - -void ConnectionModelStatementDelegate::handleFunctionChanged() -{ - QTC_ASSERT(std::holds_alternative(m_statement), - return ); - - ConnectionEditorStatements::MatchedFunction &functionStatement - = std::get(m_statement); - - functionStatement.functionName = m_functionDelegate.name(); - functionStatement.nodeId = m_functionDelegate.id(); - - emit statementChanged(); -} - -void ConnectionModelStatementDelegate::handleLhsChanged() -{ - if (m_actionType == Assign) { - QTC_ASSERT(std::holds_alternative(m_statement), - return ); - - ConnectionEditorStatements::Assignment &assignmentStatement - = std::get(m_statement); - - assignmentStatement.lhs.nodeId = m_lhsDelegate.id(); - assignmentStatement.lhs.propertyName = m_lhsDelegate.name(); - - } else if (m_actionType == SetProperty) { - QTC_ASSERT(std::holds_alternative(m_statement), - return ); - - ConnectionEditorStatements::PropertySet &setPropertyStatement - = std::get(m_statement); - - setPropertyStatement.lhs.nodeId = m_lhsDelegate.id(); - setPropertyStatement.lhs.propertyName = m_lhsDelegate.name(); - } else { - QTC_ASSERT(false, return ); - } - - emit statementChanged(); -} - -void ConnectionModelStatementDelegate::handleRhsAssignmentChanged() -{ - QTC_ASSERT(std::holds_alternative(m_statement), return ); - - ConnectionEditorStatements::Assignment &assignmentStatement - = std::get(m_statement); - - assignmentStatement.rhs.nodeId = m_rhsAssignmentDelegate.id(); - assignmentStatement.rhs.propertyName = m_rhsAssignmentDelegate.name(); - - setupPropertyType(); - - emit statementChanged(); -} - -static ConnectionEditorStatements::Literal parseTextArgument(const QString &text) -{ - if (text.startsWith("\"") && text.endsWith("\"")) { - QString ret = text; - ret.remove(0, 1); - ret.chop(1); - return ret; - } - - if (text == "true") - return true; - - if (text == "false") - return false; - - bool ok = true; - double d = text.toDouble(&ok); - if (ok) - return d; - - return text; -} - -static ConnectionEditorStatements::ComparativeStatement parseTextArgumentComparativeStatement( - const QString &text) -{ - if (text.startsWith("\"") && text.endsWith("\"")) { - QString ret = text; - ret.remove(0, 1); - ret.chop(1); - return ret; - } - - if (text == "true") - return true; - - if (text == "false") - return false; - - bool ok = true; - double d = text.toDouble(&ok); - if (ok) - return d; - - return text; -} - -static ConnectionEditorStatements::RightHandSide parseLogTextArgument(const QString &text) -{ - if (text.startsWith("\"") && text.endsWith("\"")) { - QString ret = text; - ret.remove(0, 1); - ret.chop(1); - return ret; - } - - if (text == "true") - return true; - - if (text == "false") - return true; - - bool ok = true; - double d = text.toDouble(&ok); - if (ok) - return d; - - //TODO variables and function calls - return text; -} - -void ConnectionModelStatementDelegate::handleStringArgumentChanged() -{ - if (m_actionType == SetProperty) { - QTC_ASSERT(std::holds_alternative(m_statement), - return ); - - ConnectionEditorStatements::PropertySet &propertySet - = std::get(m_statement); - - propertySet.rhs = parseTextArgument(m_stringArgument.text()); - - } else if (m_actionType == PrintMessage) { - QTC_ASSERT(std::holds_alternative(m_statement), - return ); - - ConnectionEditorStatements::ConsoleLog &consoleLog - = std::get(m_statement); - - consoleLog.argument = parseLogTextArgument(m_stringArgument.text()); - } else { - QTC_ASSERT(false, return ); - } - - emit statementChanged(); -} - -void ConnectionModelStatementDelegate::handleStateChanged() -{ - QTC_ASSERT(std::holds_alternative(m_statement), return ); - - ConnectionEditorStatements::StateSet &stateSet = std::get( - m_statement); - - QString stateName = m_states.currentText(); - if (stateName == baseStateName()) - stateName = ""; - stateSet.stateName = "\"" + stateName + "\""; - - emit statementChanged(); -} - -void ConnectionModelStatementDelegate::handleStateTargetsChanged() -{ - QTC_ASSERT(std::holds_alternative(m_statement), return ); - - ConnectionEditorStatements::StateSet &stateSet = std::get( - m_statement); - - stateSet.nodeId = m_stateTargets.currentText(); - stateSet.stateName = "\"\""; - - setupStates(); - - emit statementChanged(); -} - -void ConnectionModelStatementDelegate::setupAssignment() -{ - QTC_ASSERT(std::holds_alternative(m_statement), return ); - - const auto assignment = std::get(m_statement); - m_lhsDelegate.setup(assignment.lhs.nodeId, assignment.lhs.propertyName); - m_rhsAssignmentDelegate.setup(assignment.rhs.nodeId, assignment.rhs.propertyName); - setupPropertyType(); -} - -void ConnectionModelStatementDelegate::setupSetProperty() -{ - QTC_ASSERT(std::holds_alternative(m_statement), - return ); - - const auto propertySet = std::get(m_statement); - m_lhsDelegate.setup(propertySet.lhs.nodeId, propertySet.lhs.propertyName); - m_stringArgument.setText(ConnectionEditorStatements::toString(propertySet.rhs)); -} - -void ConnectionModelStatementDelegate::setupCallFunction() -{ - QTC_ASSERT(std::holds_alternative(m_statement), - return ); - - const auto functionStatement = std::get( - m_statement); - m_functionDelegate.setup(functionStatement.nodeId, functionStatement.functionName); -} - -void ConnectionModelStatementDelegate::setupChangeState() -{ - QTC_ASSERT(std::holds_alternative(m_statement), return ); - - QTC_ASSERT(m_model->connectionView()->isAttached(), return ); - - auto model = m_model->connectionView()->model(); - const auto items = Utils::filtered(m_model->connectionView()->allModelNodesOfType( - model->qtQuickItemMetaInfo()), - [](const ModelNode &node) { - QmlItemNode item(node); - return node.hasId() && item.isValid() - && !item.allStateNames().isEmpty(); - }); - - QStringList itemIds = Utils::transform(items, &ModelNode::id); - const auto groups = m_model->connectionView()->allModelNodesOfType( - model->qtQuickStateGroupMetaInfo()); - - const auto rootId = m_model->connectionView()->rootModelNode().id(); - itemIds.removeAll(rootId); - - QStringList groupIds = Utils::transform(groups, &ModelNode::id); - - Utils::sort(itemIds); - Utils::sort(groupIds); - - if (!rootId.isEmpty()) - groupIds.prepend(rootId); - - const QStringList stateGroupModel = groupIds + itemIds; - m_stateTargets.setModel(stateGroupModel); - - const auto stateSet = std::get(m_statement); - - m_stateTargets.setCurrentText(stateSet.nodeId); - setupStates(); -} -QString stripQuotesFromState(const QString &input) -{ - if (input.startsWith("\"") && input.endsWith("\"")) { - QString ret = input; - ret.remove(0, 1); - ret.chop(1); - return ret; - } - return input; -} -void ConnectionModelStatementDelegate::setupStates() -{ - QTC_ASSERT(std::holds_alternative(m_statement), return ); - QTC_ASSERT(m_model->connectionView()->isAttached(), return ); - - const auto stateSet = std::get(m_statement); - - const QString nodeId = m_stateTargets.currentText(); - - const ModelNode node = m_model->connectionView()->modelNodeForId(nodeId); - - QStringList states; - if (node.metaInfo().isQtQuickItem()) { - QmlItemNode item(node); - QTC_ASSERT(item.isValid(), return ); - if (item.isRootNode()) - states = item.states().names(); //model - else - states = item.allStateNames(); //instances - } else { - QmlModelStateGroup group(node); - states = group.names(); //model - } - - const QString stateName = stripQuotesFromState(stateSet.stateName); - - states.prepend(baseStateName()); - m_states.setModel(states); - if (stateName.isEmpty()) - m_states.setCurrentText(baseStateName()); + auto normalizedSource = QmlDesigner::SignalHandlerProperty::normalizedSourceWithBraces(source); + if (property.exists()) + property.toSignalHandlerProperty().setSource(normalizedSource); else - m_states.setCurrentText(stateName); + property.parentModelNode().signalHandlerProperty(property.name()).setSource(normalizedSource); } -void ConnectionModelStatementDelegate::setupPrintMessage() +SignalHandlerProperty ConnectionModelBackendDelegate::getSignalHandlerProperty() const { - QTC_ASSERT(std::holds_alternative(m_statement), return ); + ConnectionModel *model = m_model; + QTC_ASSERT(model, return {}); + QTC_ASSERT(model->connectionView()->isAttached(), return {}); - const auto consoleLog = std::get(m_statement); - m_stringArgument.setText(ConnectionEditorStatements::toString(consoleLog.argument)); -} - -void ConnectionModelStatementDelegate::setupPropertyType() -{ - PropertyTreeModel::PropertyTypes type = PropertyTreeModel::AllTypes; - - const NodeMetaInfo metaInfo = m_rhsAssignmentDelegate.propertyMetaInfo(); - - if (metaInfo.isBool()) - type = PropertyTreeModel::BoolType; - else if (metaInfo.isNumber()) - type = PropertyTreeModel::NumberType; - else if (metaInfo.isColor()) - type = PropertyTreeModel::ColorType; - else if (metaInfo.isString()) - type = PropertyTreeModel::StringType; - else if (metaInfo.isUrl()) - type = PropertyTreeModel::UrlType; - - m_lhsDelegate.setPropertyType(type); -} - -QString ConnectionModelStatementDelegate::baseStateName() const -{ - return tr("Base State"); -} - -static ConnectionEditorStatements::MatchedCondition emptyCondition; - -ConditionListModel::ConditionListModel(ConnectionModel *model) - : m_connectionModel(model) - , m_condition(emptyCondition) -{} - -int ConditionListModel::rowCount(const QModelIndex & /*parent*/) const -{ - return m_tokens.size(); -} - -QHash ConditionListModel::roleNames() const -{ - static QHash roleNames{{Qt::UserRole + 1, "type"}, {Qt::UserRole + 2, "value"}}; - return roleNames; -} - -QVariant ConditionListModel::data(const QModelIndex &index, int role) const -{ - if (index.isValid() && index.row() < rowCount()) { - if (role == Qt::UserRole + 1) { - return m_tokens.at(index.row()).type; - } else if (role == Qt::UserRole + 2) { - return m_tokens.at(index.row()).value; - } - - qCWarning(ConnectionEditorLog) << __FUNCTION__ << "invalid role"; - } else { - qCWarning(ConnectionEditorLog) << __FUNCTION__ << "invalid index"; - } - - return QVariant(); -} - -void ConditionListModel::setup() -{ - m_tokens.clear(); - - internalSetup(); - - emit validChanged(); - emit emptyChanged(); - - beginResetModel(); - endResetModel(); -} - -void ConditionListModel::setCondition(ConnectionEditorStatements::MatchedCondition &condition) -{ - m_condition = condition; - setup(); -} - -ConnectionEditorStatements::MatchedCondition &ConditionListModel::condition() -{ - return m_condition; -} - -ConditionListModel::ConditionToken ConditionListModel::tokenFromConditionToken( - const ConnectionEditorStatements::ConditionToken &token) -{ - ConditionToken ret; - ret.type = Operator; - ret.value = ConnectionEditorStatements::toJavascript(token); - - return ret; -} - -ConditionListModel::ConditionToken ConditionListModel::tokenFromComparativeStatement( - const ConnectionEditorStatements::ComparativeStatement &token) -{ - ConditionToken ret; - - if (auto *variable = std::get_if(&token)) { - ret.type = Variable; - ret.value = variable->expression(); - return ret; - } else if (auto *literal = std::get_if(&token)) { - ret.type = Literal; - ret.value = "\"" + *literal + "\""; - return ret; - } else if (auto *literal = std::get_if(&token)) { - ret.type = Literal; - if (*literal) - ret.value = "true"; - else - ret.value = "false"; - return ret; - } else if (auto *literal = std::get_if(&token)) { - ret.type = Literal; - ret.value = QString::number(*literal); - return ret; - } - - ret.type = Invalid; - ret.value = "invalid"; - return {}; -} - -void ConditionListModel::insertToken(int index, const QString &value) -{ - beginInsertRows({}, index, index); - - m_tokens.insert(index, valueToToken(value)); - validateAndRebuildTokens(); - - endInsertRows(); - //resetModel(); -} - -void ConditionListModel::updateToken(int index, const QString &value) -{ - m_tokens[index] = valueToToken(value); - validateAndRebuildTokens(); - - dataChanged(createIndex(index, 0), createIndex(index, 0)); - //resetModel(); -} - -void ConditionListModel::appendToken(const QString &value) -{ - beginInsertRows({}, rowCount() - 1, rowCount() - 1); - - insertToken(rowCount(), value); - validateAndRebuildTokens(); - - endInsertRows(); - //resetModel(); -} - -void ConditionListModel::removeToken(int index) -{ - QTC_ASSERT(index < m_tokens.count(), return ); - beginRemoveRows({}, index, index); - - m_tokens.remove(index, 1); - validateAndRebuildTokens(); - - endRemoveRows(); - - //resetModel(); -} - -void ConditionListModel::insertIntermediateToken(int index, const QString &value) -{ - beginInsertRows({}, index, index); - - ConditionToken token; - token.type = Intermediate; - token.value = value; - - m_tokens.insert(index, token); - - endInsertRows(); - //resetModel(); -} - -void ConditionListModel::insertShadowToken(int index, const QString &value) -{ - beginInsertRows({}, index, index); - - ConditionToken token; - token.type = Shadow; - token.value = value; - - m_tokens.insert(index, token); - - endInsertRows(); - - //resetModel(); -} - -void ConditionListModel::setShadowToken(int index, const QString &value) -{ - m_tokens[index].type = Shadow; - m_tokens[index].value = value; - - dataChanged(createIndex(index, 0), createIndex(index, 0)); - //resetModel(); -} - -bool ConditionListModel::valid() const -{ - return m_valid; -} - -bool ConditionListModel::empty() const -{ - return m_tokens.isEmpty(); -} - -void ConditionListModel::command(const QString &string) -{ - //TODO remove from prodcution code - QStringList list = string.split("%", Qt::SkipEmptyParts); - - if (list.size() < 2) - return; - - if (list.size() == 2) { - if (list.first() == "A") { - appendToken(list.last()); - } else if (list.first() == "R") { - bool ok = true; - int index = list.last().toInt(&ok); - - if (ok) - removeToken(index); - } - } - - if (list.size() == 3) { - if (list.first() == "U") { - bool ok = true; - int index = list.at(1).toInt(&ok); - - if (ok) - updateToken(index, list.last()); - } else if (list.first() == "I") { - bool ok = true; - int index = list.at(1).toInt(&ok); - - if (ok) - insertToken(index, list.last()); - } - } -} - -void ConditionListModel::setInvalid(const QString &errorMessage, int index) -{ - m_valid = false; - m_errorMessage = errorMessage; - - emit errorChanged(); - emit validChanged(); - - if (index != -1) { - m_errorIndex = index; - emit errorIndexChanged(); - } -} - -void ConditionListModel::setValid() -{ - m_valid = true; - m_errorMessage.clear(); - m_errorIndex = -1; - - emit errorChanged(); - emit validChanged(); - emit errorIndexChanged(); -} - -QString ConditionListModel::error() const -{ - return m_errorMessage; -} - -int ConditionListModel::errorIndex() const -{ - return m_errorIndex; -} - -bool ConditionListModel::operatorAllowed(int cursorPosition) -{ - if (m_tokens.empty()) - return false; - - int tokenIdx = cursorPosition - 1; - - if (tokenIdx >= 0 && tokenIdx < m_tokens.length() && m_tokens[tokenIdx].type != Operator) - return true; - - return false; -} - -void ConditionListModel::internalSetup() -{ - setInvalid(tr("No Valid Condition")); - if (!m_condition.statements.size() && !m_condition.tokens.size()) - return; - - if (m_condition.statements.size() != m_condition.tokens.size() + 1) - return; - - if (m_condition.statements.size() == 1 && m_condition.tokens.isEmpty()) { - auto token = tokenFromComparativeStatement(m_condition.statements.first()); - if (token.value == defaultCondition) - return; - } - - auto s_it = m_condition.statements.begin(); - auto o_it = m_condition.tokens.begin(); - - while (o_it != m_condition.tokens.end()) { - m_tokens.append(tokenFromComparativeStatement(*s_it)); - m_tokens.append(tokenFromConditionToken(*o_it)); - - s_it++; - o_it++; - } - m_tokens.append(tokenFromComparativeStatement(*s_it)); - - setValid(); -} - -ConditionListModel::ConditionToken ConditionListModel::valueToToken(const QString &value) -{ - const QStringList operators = {"&&", "||", "===", "!==", ">", ">=", "<", "<="}; - - if (operators.contains(value)) { - ConditionToken token; - token.type = Operator; - token.value = value; - return token; - } - - bool ok = false; - value.toDouble(&ok); - - if (value == "true" || value == "false" || ok - || (value.startsWith("\"") && value.endsWith("\""))) { - ConditionToken token; - token.type = Literal; - token.value = value; - return token; - } - - static QRegularExpression regexp("^[a-z_]\\w*|^[A-Z]\\w*\\.{1}([a-z_]\\w*\\.?)+"); - QRegularExpressionMatch match = regexp.match(value); - - if (match.hasMatch()) { //variable - ConditionToken token; - token.type = Variable; - token.value = value; - return token; - } - - ConditionToken token; - token.type = Invalid; - token.value = value; - - return token; -} - -void ConditionListModel::resetModel() -{ - beginResetModel(); - endResetModel(); -} - -int ConditionListModel::checkOrder() const -{ - auto it = m_tokens.begin(); - - bool wasOperator = true; - - int ret = 0; - while (it != m_tokens.end()) { - if (wasOperator && it->type == Operator) - return ret; - if (!wasOperator && it->type == Literal) - return ret; - if (!wasOperator && it->type == Variable) - return ret; - wasOperator = it->type == Operator; - it++; - ret++; - } - - if (wasOperator) - return ret; - - return -1; -} - -void ConditionListModel::validateAndRebuildTokens() -{ - /// NEW - auto it = m_tokens.begin(); - - while (it != m_tokens.end()) { - if (it->type == Intermediate) - *it = valueToToken(it->value); - - it++; - } - // NEW - - QString invalidValue; - const bool invalidToken = Utils::contains(m_tokens, - [&invalidValue](const ConditionToken &token) { - if (token.type == Invalid) - invalidValue = token.value; - return token.type == Invalid; - }); - - if (invalidToken) { - setInvalid(tr("Invalid token %1").arg(invalidValue)); - return; - } - - if (int firstError = checkOrder() != -1) { - setInvalid(tr("Invalid order at %1").arg(firstError), firstError); - return; - } - - setValid(); - - rebuildTokens(); -} - -void ConditionListModel::rebuildTokens() -{ - QTC_ASSERT(m_valid, return ); - - m_condition.statements.clear(); - m_condition.tokens.clear(); - - auto it = m_tokens.begin(); - - while (it != m_tokens.end()) { - QTC_ASSERT(it->type != Invalid, return ); - if (it->type == Operator) - m_condition.tokens.append(toOperatorStatement(*it)); - else if (it->type == Literal || it->type == Variable) - m_condition.statements.append(toStatement(*it)); - - it++; - } - - emit conditionChanged(); -} - -ConnectionEditorStatements::ConditionToken ConditionListModel::toOperatorStatement( - const ConditionToken &token) -{ - if (token.value == "&&") - return ConnectionEditorStatements::ConditionToken::And; - - if (token.value == "||") - return ConnectionEditorStatements::ConditionToken::Or; - - if (token.value == "===") - return ConnectionEditorStatements::ConditionToken::Equals; - - if (token.value == "!==") - return ConnectionEditorStatements::ConditionToken::Not; - - if (token.value == ">") - return ConnectionEditorStatements::ConditionToken::LargerThan; - - if (token.value == ">=") - return ConnectionEditorStatements::ConditionToken::LargerEqualsThan; - - if (token.value == "<") - return ConnectionEditorStatements::ConditionToken::SmallerThan; - - if (token.value == "<=") - return ConnectionEditorStatements::ConditionToken::SmallerEqualsThan; - - return ConnectionEditorStatements::ConditionToken::Unknown; -} - -ConnectionEditorStatements::ComparativeStatement ConditionListModel::toStatement( - const ConditionToken &token) -{ - if (token.type == Variable) { - QStringList list = token.value.split("."); - ConnectionEditorStatements::Variable variable; - - variable.nodeId = list.first(); - if (list.count() > 1) - variable.propertyName = list.last(); - return variable; - } else if (token.type == Literal) { - return parseTextArgumentComparativeStatement(token.value); - } - - return {}; + return model->signalHandlerPropertyForRow(currentRow()); } void QmlDesigner::ConnectionModel::modelAboutToBeDetached() diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index 017cf676843..8767dac4134 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -3,15 +3,11 @@ #pragma once -#include -#include +#include #include -#include #include -#include - namespace QmlDesigner { class AbstractProperty; @@ -23,247 +19,35 @@ class VariantProperty; class ConnectionView; class ConnectionModel; -class ConditionListModel : public QAbstractListModel +class ConnectionModelBackendDelegate : public ScriptEditorBackend { 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 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 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(ActionType actionType READ actionType NOTIFY actionTypeChanged) 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: explicit ConnectionModelBackendDelegate(ConnectionModel *model); - using ActionType = ConnectionModelStatementDelegate::ActionType; - - 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 update() override; void setCurrentRow(int i); - void update(); - Q_INVOKABLE void jumpToCode(); + PropertyTreeModelDelegate *signal(); signals: void currentRowChanged(); - void actionTypeChanged(); - void hasConditionChanged(); - void hasElseChanged(); - void sourceChanged(); - void popupShouldClose(); - void popupShouldOpen(); + +private slots: + void handleTargetChanged(); private: + AbstractProperty getSourceProperty() const override; + void setPropertySource(const QString &source) override; + + SignalHandlerProperty getSignalHandlerProperty() 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; - ConnectionEditorStatements::Handler m_handler; 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 m_model = nullptr; }; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index a60edca67b9..007116105b0 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -83,10 +83,8 @@ public: 0, "DynamicPropertiesModelBackendDelegate"); - qmlRegisterType("ConnectionsEditorEditorBackend", - 1, - 0, - "ConnectionModelStatementDelegate"); + // TODO: register in the property editor along with the ScriptEditorBackend + qmlRegisterType("ScriptEditorBackend", 1, 0, "StatementDelegate"); qmlRegisterType("ConnectionsEditorEditorBackend", 1, 0, "ConditionListModel"); diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.cpp index a45b0e115d8..1694231630c 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "dynamicpropertiesitem.h" -#include "connectioneditorutils.h" +#include #include #include diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp index 687752b8c98..c4307a07182 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp @@ -2,22 +2,23 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "dynamicpropertiesmodel.h" +#include "connectioneditorlogging.h" #include "dynamicpropertiesitem.h" -#include "connectioneditorutils.h" #include #include #include -#include -#include -#include -#include -#include #include #include #include #include #include +#include +#include +#include +#include +#include +#include #include diff --git a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp index 8ba6b22ac0f..8c6f272fa91 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp @@ -27,7 +27,7 @@ #include "bindingproperty.h" #include "propertyeditorvalue.h" -#include "connectioneditorutils.h" +#include #include diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/scripteditor/propertytreemodel.cpp similarity index 98% rename from src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp rename to src/plugins/qmldesigner/components/scripteditor/propertytreemodel.cpp index 463286e6956..b1892c9b557 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/scripteditor/propertytreemodel.cpp @@ -163,8 +163,8 @@ std::vector properityLists() return result; } -PropertyTreeModel::PropertyTreeModel(ConnectionView *view) - : m_connectionView(view) +PropertyTreeModel::PropertyTreeModel(AbstractView *view) + : m_view(view) {} 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 { auto internalId = parent.internalId(); - if (!m_connectionView->isAttached()) + if (!m_view->isAttached()) return {}; if (!parent.isValid()) @@ -337,7 +337,7 @@ QPersistentModelIndex PropertyTreeModel::indexForInternalIdAndRow(quintptr inter int PropertyTreeModel::rowCount(const QModelIndex &parent) const { - if (!m_connectionView->isAttached() || parent.column() > 0) + if (!m_view->isAttached() || parent.column() > 0) return 0; if (!parent.isValid()) @@ -404,10 +404,10 @@ const std::vector PropertyTreeModel::getProperties(const ModelNode ModelNode PropertyTreeModel::getModelNodeForId(const QString &id) const { - if (!m_connectionView->isAttached()) + if (!m_view->isAttached()) return {}; - return m_connectionView->modelNodeForId(id); + return m_view->modelNodeForId(id); } QModelIndex PropertyTreeModel::ensureModelIndex(const ModelNode &node, int row) const @@ -473,10 +473,10 @@ void PropertyTreeModel::testModel() const QList PropertyTreeModel::allModelNodesWithIdsSortedByDisplayName() const { - if (!m_connectionView->isAttached()) + if (!m_view->isAttached()) return {}; - return Utils::sorted(ModelUtils::allModelNodesWithId(m_connectionView), + return Utils::sorted(ModelUtils::allModelNodesWithId(m_view), [](const ModelNode &lhs, const ModelNode &rhs) { return lhs.displayName() < rhs.displayName(); }); @@ -882,7 +882,7 @@ QString PropertyListProxyModel::parentName() const return m_treeModel->data(m_parentIndex, PropertyTreeModel::UserRoles::PropertyNameRole).toString(); } -PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *view) +PropertyTreeModelDelegate::PropertyTreeModelDelegate(AbstractView *view) : m_model(view) { connect(&m_nameCombboBox, &StudioQmlComboBoxBackend::activated, this, [this] { diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h b/src/plugins/qmldesigner/components/scripteditor/propertytreemodel.h similarity index 97% rename from src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h rename to src/plugins/qmldesigner/components/scripteditor/propertytreemodel.h index 39af4857654..bb7dab533db 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h +++ b/src/plugins/qmldesigner/components/scripteditor/propertytreemodel.h @@ -49,7 +49,8 @@ public: BoolType }; - PropertyTreeModel(ConnectionView *view); + // PropertyTreeModel(ConnectionView *view); + PropertyTreeModel(AbstractView *view); void resetModel(); @@ -120,7 +121,7 @@ private: const PropertyMetaInfo &metaInfo, bool recursive) const; - ConnectionView *m_connectionView; + AbstractView *m_view; mutable std::set m_indexCache; mutable std::vector m_indexHash; @@ -187,7 +188,7 @@ class PropertyTreeModelDelegate : public QObject Q_PROPERTY(StudioQmlComboBoxBackend *id READ idCombboBox CONSTANT) public: - explicit PropertyTreeModelDelegate(ConnectionView *view); + explicit PropertyTreeModelDelegate(AbstractView *view); void setPropertyType(PropertyTreeModel::PropertyTypes type); void setup(const QString &id, const QString &name, bool *nameExists = nullptr); void setupNameComboBox(const QString &id, const QString &name, bool *nameExists); diff --git a/src/plugins/qmldesigner/components/scripteditor/scripteditorbackend.cpp b/src/plugins/qmldesigner/components/scripteditor/scripteditorbackend.cpp new file mode 100644 index 00000000000..4846f149ddf --- /dev/null +++ b/src/plugins/qmldesigner/components/scripteditor/scripteditorbackend.cpp @@ -0,0 +1,1373 @@ +// 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 "scripteditorbackend.h" +#include "scripteditorevaluator.h" +#include "scripteditorutils.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace { +const char defaultCondition[] = "condition"; + +QString getSourceFromProperty(const QmlDesigner::AbstractProperty &property) +{ + QTC_ASSERT(property.isValid(), return {}); + + if (!property.exists()) + return {}; + + if (property.isSignalHandlerProperty()) + return property.toSignalHandlerProperty().source(); + if (property.isBindingProperty()) + return property.toBindingProperty().expression(); + + return {}; +} +} // namespace + +namespace QmlDesigner { +static ScriptEditorStatements::MatchedCondition emptyCondition; + +ConditionListModel::ConditionListModel(AbstractView *view) + : m_view(view) + , m_condition(emptyCondition) +{} + +int ConditionListModel::rowCount(const QModelIndex & /*parent*/) const +{ + return m_tokens.size(); +} + +QHash ConditionListModel::roleNames() const +{ + static QHash roleNames{{Qt::UserRole + 1, "type"}, {Qt::UserRole + 2, "value"}}; + return roleNames; +} + +QVariant ConditionListModel::data(const QModelIndex &index, int role) const +{ + if (index.isValid() && index.row() < rowCount()) { + if (role == Qt::UserRole + 1) { + return m_tokens.at(index.row()).type; + } else if (role == Qt::UserRole + 2) { + return m_tokens.at(index.row()).value; + } + + qCWarning(ScriptEditorLog) << __FUNCTION__ << "invalid role"; + } else { + qCWarning(ScriptEditorLog) << __FUNCTION__ << "invalid index"; + } + + return QVariant(); +} + +void ConditionListModel::setup() +{ + m_tokens.clear(); + + internalSetup(); + + emit validChanged(); + emit emptyChanged(); + + beginResetModel(); + endResetModel(); +} + +void ConditionListModel::setCondition(ScriptEditorStatements::MatchedCondition &condition) +{ + m_condition = condition; + setup(); +} + +ScriptEditorStatements::MatchedCondition &ConditionListModel::condition() +{ + return m_condition; +} + +ConditionListModel::ConditionToken ConditionListModel::tokenFromConditionToken( + const ScriptEditorStatements::ConditionToken &token) +{ + ConditionToken ret; + ret.type = Operator; + ret.value = ScriptEditorStatements::toJavascript(token); + + return ret; +} + +ConditionListModel::ConditionToken ConditionListModel::tokenFromComparativeStatement( + const ScriptEditorStatements::ComparativeStatement &token) +{ + ConditionToken ret; + + if (auto *variable = std::get_if(&token)) { + ret.type = Variable; + ret.value = variable->expression(); + return ret; + } else if (auto *literal = std::get_if(&token)) { + ret.type = Literal; + ret.value = "\"" + *literal + "\""; + return ret; + } else if (auto *literal = std::get_if(&token)) { + ret.type = Literal; + if (*literal) + ret.value = "true"; + else + ret.value = "false"; + return ret; + } else if (auto *literal = std::get_if(&token)) { + ret.type = Literal; + ret.value = QString::number(*literal); + return ret; + } + + ret.type = Invalid; + ret.value = "invalid"; + return {}; +} + +void ConditionListModel::insertToken(int index, const QString &value) +{ + beginInsertRows({}, index, index); + + m_tokens.insert(index, valueToToken(value)); + validateAndRebuildTokens(); + + endInsertRows(); +} + +void ConditionListModel::updateToken(int index, const QString &value) +{ + m_tokens[index] = valueToToken(value); + validateAndRebuildTokens(); + + emit dataChanged(createIndex(index, 0), createIndex(index, 0)); +} + +void ConditionListModel::appendToken(const QString &value) +{ + beginInsertRows({}, rowCount() - 1, rowCount() - 1); + + insertToken(rowCount(), value); + validateAndRebuildTokens(); + + endInsertRows(); +} + +void ConditionListModel::removeToken(int index) +{ + QTC_ASSERT(index < m_tokens.count(), return); + beginRemoveRows({}, index, index); + + m_tokens.remove(index, 1); + validateAndRebuildTokens(); + + endRemoveRows(); +} + +void ConditionListModel::insertIntermediateToken(int index, const QString &value) +{ + beginInsertRows({}, index, index); + + ConditionToken token; + token.type = Intermediate; + token.value = value; + + m_tokens.insert(index, token); + + endInsertRows(); +} + +void ConditionListModel::insertShadowToken(int index, const QString &value) +{ + beginInsertRows({}, index, index); + + ConditionToken token; + token.type = Shadow; + token.value = value; + + m_tokens.insert(index, token); + + endInsertRows(); +} + +void ConditionListModel::setShadowToken(int index, const QString &value) +{ + m_tokens[index].type = Shadow; + m_tokens[index].value = value; + + emit dataChanged(createIndex(index, 0), createIndex(index, 0)); +} + +bool ConditionListModel::valid() const +{ + return m_valid; +} + +bool ConditionListModel::empty() const +{ + return m_tokens.isEmpty(); +} + +void ConditionListModel::command(const QString &string) +{ + //TODO remove from prodcution code + QStringList list = string.split("%", Qt::SkipEmptyParts); + + if (list.size() < 2) + return; + + if (list.size() == 2) { + if (list.first() == "A") { + appendToken(list.last()); + } else if (list.first() == "R") { + bool ok = true; + int index = list.last().toInt(&ok); + + if (ok) + removeToken(index); + } + } + + if (list.size() == 3) { + if (list.first() == "U") { + bool ok = true; + int index = list.at(1).toInt(&ok); + + if (ok) + updateToken(index, list.last()); + } else if (list.first() == "I") { + bool ok = true; + int index = list.at(1).toInt(&ok); + + if (ok) + insertToken(index, list.last()); + } + } +} + +void ConditionListModel::setInvalid(const QString &errorMessage, int index) +{ + m_valid = false; + m_errorMessage = errorMessage; + + emit errorChanged(); + emit validChanged(); + + if (index != -1) { + m_errorIndex = index; + emit errorIndexChanged(); + } +} + +void ConditionListModel::setValid() +{ + m_valid = true; + m_errorMessage.clear(); + m_errorIndex = -1; + + emit errorChanged(); + emit validChanged(); + emit errorIndexChanged(); +} + +QString ConditionListModel::error() const +{ + return m_errorMessage; +} + +int ConditionListModel::errorIndex() const +{ + return m_errorIndex; +} + +bool ConditionListModel::operatorAllowed(int cursorPosition) +{ + if (m_tokens.empty()) + return false; + + int tokenIdx = cursorPosition - 1; + + if (tokenIdx >= 0 && tokenIdx < m_tokens.length() && m_tokens[tokenIdx].type != Operator) + return true; + + return false; +} + +void ConditionListModel::internalSetup() +{ + setInvalid(tr("No Valid Condition")); + if (!m_condition.statements.size() && !m_condition.tokens.size()) + return; + + if (m_condition.statements.size() != m_condition.tokens.size() + 1) + return; + + if (m_condition.statements.size() == 1 && m_condition.tokens.isEmpty()) { + auto token = tokenFromComparativeStatement(m_condition.statements.first()); + if (token.value == defaultCondition) + return; + } + + auto s_it = m_condition.statements.begin(); + auto o_it = m_condition.tokens.begin(); + + while (o_it != m_condition.tokens.end()) { + m_tokens.append(tokenFromComparativeStatement(*s_it)); + m_tokens.append(tokenFromConditionToken(*o_it)); + + s_it++; + o_it++; + } + m_tokens.append(tokenFromComparativeStatement(*s_it)); + + setValid(); +} + +ConditionListModel::ConditionToken ConditionListModel::valueToToken(const QString &value) +{ + const QStringList operators = {"&&", "||", "===", "!==", ">", ">=", "<", "<="}; + + if (operators.contains(value)) { + ConditionToken token; + token.type = Operator; + token.value = value; + return token; + } + + bool ok = false; + value.toDouble(&ok); + + if (value == "true" || value == "false" || ok || (value.startsWith("\"") && value.endsWith("\""))) { + ConditionToken token; + token.type = Literal; + token.value = value; + return token; + } + + static QRegularExpression regexp("^[a-z_]\\w*|^[A-Z]\\w*\\.{1}([a-z_]\\w*\\.?)+"); + QRegularExpressionMatch match = regexp.match(value); + + if (match.hasMatch()) { //variable + ConditionToken token; + token.type = Variable; + token.value = value; + return token; + } + + ConditionToken token; + token.type = Invalid; + token.value = value; + + return token; +} + +void ConditionListModel::resetModel() +{ + beginResetModel(); + endResetModel(); +} + +int ConditionListModel::checkOrder() const +{ + auto it = m_tokens.begin(); + + bool wasOperator = true; + + int ret = 0; + while (it != m_tokens.end()) { + if (wasOperator && it->type == Operator) + return ret; + if (!wasOperator && it->type == Literal) + return ret; + if (!wasOperator && it->type == Variable) + return ret; + wasOperator = it->type == Operator; + it++; + ret++; + } + + if (wasOperator) + return ret; + + return -1; +} + +void ConditionListModel::validateAndRebuildTokens() +{ + /// NEW + auto it = m_tokens.begin(); + + while (it != m_tokens.end()) { + if (it->type == Intermediate) + *it = valueToToken(it->value); + + it++; + } + // NEW + + QString invalidValue; + const bool invalidToken = Utils::contains(m_tokens, [&invalidValue](const ConditionToken &token) { + if (token.type == Invalid) + invalidValue = token.value; + return token.type == Invalid; + }); + + if (invalidToken) { + setInvalid(tr("Invalid token %1").arg(invalidValue)); + return; + } + + if (int firstError = checkOrder() != -1) { + setInvalid(tr("Invalid order at %1").arg(firstError), firstError); + return; + } + + setValid(); + + rebuildTokens(); +} + +ScriptEditorStatements::ConditionToken ConditionListModel::toOperatorStatement(const ConditionToken &token) +{ + if (token.value == "&&") + return ScriptEditorStatements::ConditionToken::And; + + if (token.value == "||") + return ScriptEditorStatements::ConditionToken::Or; + + if (token.value == "===") + return ScriptEditorStatements::ConditionToken::Equals; + + if (token.value == "!==") + return ScriptEditorStatements::ConditionToken::Not; + + if (token.value == ">") + return ScriptEditorStatements::ConditionToken::LargerThan; + + if (token.value == ">=") + return ScriptEditorStatements::ConditionToken::LargerEqualsThan; + + if (token.value == "<") + return ScriptEditorStatements::ConditionToken::SmallerThan; + + if (token.value == "<=") + return ScriptEditorStatements::ConditionToken::SmallerEqualsThan; + + return ScriptEditorStatements::ConditionToken::Unknown; +} + +static ScriptEditorStatements::ComparativeStatement parseTextArgumentComparativeStatement( + const QString &text) +{ + if (text.startsWith("\"") && text.endsWith("\"")) { + QString ret = text; + ret.remove(0, 1); + ret.chop(1); + return ret; + } + + if (text == "true") + return true; + + if (text == "false") + return false; + + bool ok = true; + double d = text.toDouble(&ok); + if (ok) + return d; + + return text; +} + +ScriptEditorStatements::ComparativeStatement ConditionListModel::toStatement(const ConditionToken &token) +{ + if (token.type == Variable) { + QStringList list = token.value.split("."); + ScriptEditorStatements::Variable variable; + + variable.nodeId = list.first(); + if (list.count() > 1) + variable.propertyName = list.last(); + return variable; + } else if (token.type == Literal) { + return parseTextArgumentComparativeStatement(token.value); + } + + return {}; +} + +void ConditionListModel::rebuildTokens() +{ + QTC_ASSERT(m_valid, return); + + m_condition.statements.clear(); + m_condition.tokens.clear(); + + auto it = m_tokens.begin(); + + while (it != m_tokens.end()) { + QTC_ASSERT(it->type != Invalid, return); + if (it->type == Operator) + m_condition.tokens.append(toOperatorStatement(*it)); + else if (it->type == Literal || it->type == Variable) + m_condition.statements.append(toStatement(*it)); + + it++; + } + + emit conditionChanged(); +} + +static ScriptEditorStatements::MatchedStatement emptyStatement; + +StatementDelegate::StatementDelegate(AbstractView *view) + : m_functionDelegate(view) + , m_lhsDelegate(view) + , m_rhsAssignmentDelegate(view) + , m_statement(emptyStatement) + , m_view(view) +{ + m_functionDelegate.setPropertyType(PropertyTreeModel::SlotType); + + connect(&m_functionDelegate, &PropertyTreeModelDelegate::commitData, this, [this] { + handleFunctionChanged(); + }); + + connect(&m_rhsAssignmentDelegate, &PropertyTreeModelDelegate::commitData, this, [this] { + handleRhsAssignmentChanged(); + }); + + connect(&m_lhsDelegate, &PropertyTreeModelDelegate::commitData, this, [this] { + handleLhsChanged(); + }); + + connect(&m_stringArgument, &StudioQmlTextBackend::activated, this, [this] { + handleStringArgumentChanged(); + }); + + connect(&m_states, &StudioQmlComboBoxBackend::activated, this, [this] { handleStateChanged(); }); + + connect(&m_stateTargets, &StudioQmlComboBoxBackend::activated, this, [this] { + handleStateTargetsChanged(); + }); +} + +void StatementDelegate::setActionType(ActionType type) +{ + if (m_actionType == type) + return; + + m_actionType = type; + emit actionTypeChanged(); + setup(); +} + +void StatementDelegate::setup() +{ + switch (m_actionType) { + case CallFunction: + setupCallFunction(); + break; + case Assign: + setupAssignment(); + break; + case ChangeState: + setupChangeState(); + break; + case SetProperty: + setupSetProperty(); + break; + case PrintMessage: + setupPrintMessage(); + break; + case Custom: + break; + }; +} + +void StatementDelegate::setStatement(ScriptEditorStatements::MatchedStatement &statement) +{ + m_statement = statement; + setup(); +} + +ScriptEditorStatements::MatchedStatement &StatementDelegate::statement() +{ + return m_statement; +} + +StatementDelegate::ActionType StatementDelegate::actionType() const +{ + return m_actionType; +} + +PropertyTreeModelDelegate *StatementDelegate::function() +{ + return &m_functionDelegate; +} + +PropertyTreeModelDelegate *StatementDelegate::lhs() +{ + return &m_lhsDelegate; +} + +PropertyTreeModelDelegate *StatementDelegate::rhsAssignment() +{ + return &m_rhsAssignmentDelegate; +} + +StudioQmlTextBackend *StatementDelegate::stringArgument() +{ + return &m_stringArgument; +} + +StudioQmlComboBoxBackend *StatementDelegate::stateTargets() +{ + return &m_stateTargets; +} + +StudioQmlComboBoxBackend *StatementDelegate::states() +{ + return &m_states; +} + +void StatementDelegate::handleFunctionChanged() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return); + + ScriptEditorStatements::MatchedFunction &functionStatement = std::get( + m_statement); + + functionStatement.functionName = m_functionDelegate.name(); + functionStatement.nodeId = m_functionDelegate.id(); + + emit statementChanged(); +} + +void StatementDelegate::handleLhsChanged() +{ + if (m_actionType == Assign) { + QTC_ASSERT(std::holds_alternative(m_statement), return); + + ScriptEditorStatements::Assignment &assignmentStatement = std::get( + m_statement); + + assignmentStatement.lhs.nodeId = m_lhsDelegate.id(); + assignmentStatement.lhs.propertyName = m_lhsDelegate.name(); + + } else if (m_actionType == SetProperty) { + QTC_ASSERT(std::holds_alternative(m_statement), return); + + ScriptEditorStatements::PropertySet &setPropertyStatement = std::get( + m_statement); + + setPropertyStatement.lhs.nodeId = m_lhsDelegate.id(); + setPropertyStatement.lhs.propertyName = m_lhsDelegate.name(); + } else { + QTC_ASSERT(false, return); + } + + emit statementChanged(); +} + +void StatementDelegate::handleRhsAssignmentChanged() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return); + + ScriptEditorStatements::Assignment &assignmentStatement = std::get( + m_statement); + + assignmentStatement.rhs.nodeId = m_rhsAssignmentDelegate.id(); + assignmentStatement.rhs.propertyName = m_rhsAssignmentDelegate.name(); + + setupPropertyType(); + + emit statementChanged(); +} + +static ScriptEditorStatements::Literal parseTextArgument(const QString &text) +{ + if (text.startsWith("\"") && text.endsWith("\"")) { + QString ret = text; + ret.remove(0, 1); + ret.chop(1); + return ret; + } + + if (text == "true") + return true; + + if (text == "false") + return false; + + bool ok = true; + double d = text.toDouble(&ok); + if (ok) + return d; + + return text; +} + +static ScriptEditorStatements::RightHandSide parseLogTextArgument(const QString &text) +{ + if (text.startsWith("\"") && text.endsWith("\"")) { + QString ret = text; + ret.remove(0, 1); + ret.chop(1); + return ret; + } + + if (text == "true") + return true; + + if (text == "false") + return true; + + bool ok = true; + double d = text.toDouble(&ok); + if (ok) + return d; + + //TODO variables and function calls + return text; +} + +void StatementDelegate::handleStringArgumentChanged() +{ + if (m_actionType == SetProperty) { + QTC_ASSERT(std::holds_alternative(m_statement), return); + + ScriptEditorStatements::PropertySet &propertySet = std::get( + m_statement); + + propertySet.rhs = parseTextArgument(m_stringArgument.text()); + + } else if (m_actionType == PrintMessage) { + QTC_ASSERT(std::holds_alternative(m_statement), return); + + ScriptEditorStatements::ConsoleLog &consoleLog = std::get( + m_statement); + + consoleLog.argument = parseLogTextArgument(m_stringArgument.text()); + } else { + QTC_ASSERT(false, return); + } + + emit statementChanged(); +} + +void StatementDelegate::handleStateChanged() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return); + + ScriptEditorStatements::StateSet &stateSet = std::get( + m_statement); + + QString stateName = m_states.currentText(); + if (stateName == baseStateName()) + stateName = ""; + stateSet.stateName = "\"" + stateName + "\""; + + emit statementChanged(); +} + +void StatementDelegate::handleStateTargetsChanged() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return); + + ScriptEditorStatements::StateSet &stateSet = std::get( + m_statement); + + stateSet.nodeId = m_stateTargets.currentText(); + stateSet.stateName = "\"\""; + + setupStates(); + + emit statementChanged(); +} + +void StatementDelegate::setupAssignment() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return); + + const auto assignment = std::get(m_statement); + m_lhsDelegate.setup(assignment.lhs.nodeId, assignment.lhs.propertyName); + m_rhsAssignmentDelegate.setup(assignment.rhs.nodeId, assignment.rhs.propertyName); + setupPropertyType(); +} + +void StatementDelegate::setupSetProperty() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return); + + const auto propertySet = std::get(m_statement); + m_lhsDelegate.setup(propertySet.lhs.nodeId, propertySet.lhs.propertyName); + m_stringArgument.setText(ScriptEditorStatements::toString(propertySet.rhs)); +} + +void StatementDelegate::setupCallFunction() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return); + + const auto functionStatement = std::get(m_statement); + m_functionDelegate.setup(functionStatement.nodeId, functionStatement.functionName); +} + +void StatementDelegate::setupChangeState() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return); + + QTC_ASSERT(m_view->isAttached(), return); + + auto model = m_view->model(); + const auto items = Utils::filtered(m_view->allModelNodesOfType(model->qtQuickItemMetaInfo()), + [](const ModelNode &node) { + QmlItemNode item(node); + return node.hasId() && item.isValid() + && !item.allStateNames().isEmpty(); + }); + + QStringList itemIds = Utils::transform(items, &ModelNode::id); + const auto groups = m_view->allModelNodesOfType(model->qtQuickStateGroupMetaInfo()); + + const auto rootId = m_view->rootModelNode().id(); + itemIds.removeAll(rootId); + + QStringList groupIds = Utils::transform(groups, &ModelNode::id); + + Utils::sort(itemIds); + Utils::sort(groupIds); + + if (!rootId.isEmpty()) + groupIds.prepend(rootId); + + const QStringList stateGroupModel = groupIds + itemIds; + m_stateTargets.setModel(stateGroupModel); + + const auto stateSet = std::get(m_statement); + + m_stateTargets.setCurrentText(stateSet.nodeId); + setupStates(); +} + +static QString stripQuotesFromState(const QString &input) +{ + if (input.startsWith("\"") && input.endsWith("\"")) { + QString ret = input; + ret.remove(0, 1); + ret.chop(1); + return ret; + } + return input; +} + +void StatementDelegate::setupStates() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return); + QTC_ASSERT(m_view->isAttached(), return); + + const auto stateSet = std::get(m_statement); + + const QString nodeId = m_stateTargets.currentText(); + + const ModelNode node = m_view->modelNodeForId(nodeId); + + QStringList states; + if (node.metaInfo().isQtQuickItem()) { + QmlItemNode item(node); + QTC_ASSERT(item.isValid(), return); + if (item.isRootNode()) + states = item.states().names(); //model + else + states = item.allStateNames(); //instances + } else { + QmlModelStateGroup group(node); + states = group.names(); //model + } + + const QString stateName = stripQuotesFromState(stateSet.stateName); + + states.prepend(baseStateName()); + m_states.setModel(states); + if (stateName.isEmpty()) + m_states.setCurrentText(baseStateName()); + else + m_states.setCurrentText(stateName); +} + +void StatementDelegate::setupPrintMessage() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return); + + const auto consoleLog = std::get(m_statement); + m_stringArgument.setText(ScriptEditorStatements::toString(consoleLog.argument)); +} + +void StatementDelegate::setupPropertyType() +{ + PropertyTreeModel::PropertyTypes type = PropertyTreeModel::AllTypes; + + const NodeMetaInfo metaInfo = m_rhsAssignmentDelegate.propertyMetaInfo(); + + if (metaInfo.isBool()) + type = PropertyTreeModel::BoolType; + else if (metaInfo.isNumber()) + type = PropertyTreeModel::NumberType; + else if (metaInfo.isColor()) + type = PropertyTreeModel::ColorType; + else if (metaInfo.isString()) + type = PropertyTreeModel::StringType; + else if (metaInfo.isUrl()) + type = PropertyTreeModel::UrlType; + + m_lhsDelegate.setPropertyType(type); +} + +QString StatementDelegate::baseStateName() const +{ + return tr("Base State"); +} + +ScriptEditorBackend::ScriptEditorBackend(AbstractView *view) + : m_okStatementDelegate(view) + , m_koStatementDelegate(view) + , m_conditionListModel(view) + , m_propertyTreeModel(view) + , m_propertyListProxyModel(&m_propertyTreeModel) + , m_view(view) +{ + connect(&m_okStatementDelegate, &StatementDelegate::statementChanged, this, [this] { + handleOkStatementChanged(); + }); + + connect(&m_koStatementDelegate, &StatementDelegate::statementChanged, this, [this] { + handleKOStatementChanged(); + }); + + connect(&m_conditionListModel, &ConditionListModel::conditionChanged, this, [this] { + handleConditionChanged(); + }); +} + +static QString generateDefaultStatement(ScriptEditorBackend::ActionType actionType, + const QString &rootId) +{ + switch (actionType) { + case StatementDelegate::CallFunction: + return "Qt.quit()"; + case StatementDelegate::Assign: + return QString("%1.visible = %1.visible").arg(rootId); + case StatementDelegate::ChangeState: + return QString("%1.state = \"\"").arg(rootId); + case StatementDelegate::SetProperty: + return QString("%1.visible = true").arg(rootId); + case StatementDelegate::PrintMessage: + return QString("console.log(\"test\")").arg(rootId); + case StatementDelegate::Custom: + return {}; + }; + + return {}; +} + +void ScriptEditorBackend::changeActionType(ActionType actionType) +{ + QTC_ASSERT(actionType != StatementDelegate::Custom, return); + + AbstractView *view = m_view; + + QTC_ASSERT(view, return); + QTC_ASSERT(view->isAttached(), return); + + view->executeInTransaction("ScriptEditorBackend::removeCondition", [&]() { + ScriptEditorStatements::MatchedStatement &okStatement = ScriptEditorStatements::okStatement( + m_handler); + + ScriptEditorStatements::MatchedStatement &koStatement = ScriptEditorStatements::koStatement( + m_handler); + + koStatement = ScriptEditorStatements::EmptyBlock(); + + //We expect a valid id on the root node + const QString validId = view->rootModelNode().validId(); + QString statementSource = generateDefaultStatement(actionType, validId); + + auto tempHandler = ScriptEditorEvaluator::parseStatement(statementSource); //what's that? + + auto newOkStatement = ScriptEditorStatements::okStatement(tempHandler); + + QTC_ASSERT(!ScriptEditorStatements::isEmptyStatement(newOkStatement), return); + + okStatement = newOkStatement; + + QString newSource = ScriptEditorStatements::toJavascript(m_handler); + + setPropertySource(newSource); + }); + + setSource(getSourceFromProperty(getSourceProperty())); + + setupHandlerAndStatements(); + setupCondition(); +} + +void ScriptEditorBackend::addCondition() +{ + ScriptEditorStatements::MatchedStatement okStatement = ScriptEditorStatements::okStatement( + m_handler); + + ScriptEditorStatements::MatchedCondition newCondition; + + ScriptEditorStatements::Variable variable; + variable.nodeId = defaultCondition; + newCondition.statements.append(variable); + + ScriptEditorStatements::ConditionalStatement conditionalStatement; + + conditionalStatement.ok = okStatement; + conditionalStatement.condition = newCondition; + + m_handler = conditionalStatement; + + QString newSource = ScriptEditorStatements::toJavascript(m_handler); + + commitNewSource(newSource); + + setupHandlerAndStatements(); + setupCondition(); +} + +void ScriptEditorBackend::removeCondition() +{ + ScriptEditorStatements::MatchedStatement okStatement = ScriptEditorStatements::okStatement( + m_handler); + + m_handler = okStatement; + + QString newSource = ScriptEditorStatements::toJavascript(m_handler); + + commitNewSource(newSource); + + setupHandlerAndStatements(); + setupCondition(); +} + +void ScriptEditorBackend::addElse() +{ + ScriptEditorStatements::MatchedStatement okStatement = ScriptEditorStatements::okStatement( + m_handler); + + auto &condition = ScriptEditorStatements::conditionalStatement(m_handler); + condition.ko = condition.ok; + + QString newSource = ScriptEditorStatements::toJavascript(m_handler); + + commitNewSource(newSource); + setupHandlerAndStatements(); +} + +void ScriptEditorBackend::removeElse() +{ + ScriptEditorStatements::MatchedStatement okStatement = ScriptEditorStatements::okStatement( + m_handler); + + auto &condition = ScriptEditorStatements::conditionalStatement(m_handler); + condition.ko = ScriptEditorStatements::EmptyBlock(); + + QString newSource = ScriptEditorStatements::toJavascript(m_handler); + + commitNewSource(newSource); + setupHandlerAndStatements(); +} + +void ScriptEditorBackend::setNewSource(const QString &newSource) +{ + setSource(newSource); + commitNewSource(newSource); + setupHandlerAndStatements(); + setupCondition(); +} + +void ScriptEditorBackend::update() +{ + if (m_blockReflection) + return; + + m_propertyTreeModel.resetModel(); + m_propertyListProxyModel.setRowAndInternalId(0, internalRootIndex); + + AbstractView *view = m_view; + + QTC_ASSERT(view, return); + if (!view->isAttached()) + return; + + // setup print message as default action if property does not exists + auto sourceProperty = getSourceProperty(); + if (sourceProperty.exists()) + setSource(getSourceFromProperty(sourceProperty)); + else + setSource(QStringLiteral("console.log(\"%1\")").arg(sourceProperty.parentModelNode().id())); + + setupHandlerAndStatements(); + + setupCondition(); +} + +void ScriptEditorBackend::jumpToCode() +{ + AbstractView *view = m_view; + + QTC_ASSERT(view, return); + QTC_ASSERT(view->isAttached(), return); + auto sourceProperty = getSourceProperty(); + + ModelNodeOperations::jumpToCode(sourceProperty.parentModelNode()); +} + +bool ScriptEditorBackend::blockReflection() const +{ + return m_blockReflection; +} + +BindingProperty ScriptEditorBackend::getBindingProperty() const +{ + AbstractView *view = m_view; + + QTC_ASSERT(view, return {}); + QTC_ASSERT(view->isAttached(), return {}); + + return SelectionContext{view}.currentSingleSelectedNode().bindingProperty("script"); +} + +void ScriptEditorBackend::handleException() +{ + QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); +} + +bool ScriptEditorBackend::hasCondition() const +{ + return m_hasCondition; +} + +bool ScriptEditorBackend::hasElse() const +{ + return m_hasElse; +} + +void ScriptEditorBackend::setHasCondition(bool b) +{ + if (b == m_hasCondition) + return; + + m_hasCondition = b; + emit hasConditionChanged(); +} + +void ScriptEditorBackend::setHasElse(bool b) +{ + if (b == m_hasElse) + return; + + m_hasElse = b; + emit hasElseChanged(); +} + +ScriptEditorBackend::ActionType ScriptEditorBackend::actionType() const +{ + return m_actionType; +} + +AbstractProperty ScriptEditorBackend::getSourceProperty() const +{ + return getBindingProperty(); +} + +StatementDelegate *ScriptEditorBackend::okStatement() +{ + return &m_okStatementDelegate; +} + +StatementDelegate *ScriptEditorBackend::koStatement() +{ + return &m_koStatementDelegate; +} + +ConditionListModel *ScriptEditorBackend::conditionListModel() +{ + return &m_conditionListModel; +} + +QString ScriptEditorBackend::indentedSource() const +{ + if (m_source.isEmpty()) + return {}; + + QTextDocument doc(m_source); + IndentingTextEditModifier mod(&doc); + + mod.indent(0, m_source.length() - 1); + return mod.text(); +} + +QString ScriptEditorBackend::source() const +{ + return m_source; +} + +void ScriptEditorBackend::setSource(const QString &source) +{ + if (source == m_source) + return; + + m_source = source; + emit sourceChanged(); +} + +PropertyTreeModel *ScriptEditorBackend::propertyTreeModel() +{ + return &m_propertyTreeModel; +} + +PropertyListProxyModel *ScriptEditorBackend::propertyListProxyModel() +{ + return &m_propertyListProxyModel; +} + +void ScriptEditorBackend::setupCondition() +{ + auto &condition = ScriptEditorStatements::matchedCondition(m_handler); + m_conditionListModel.setCondition(ScriptEditorStatements::matchedCondition(m_handler)); + setHasCondition(!condition.statements.isEmpty()); +} + +void ScriptEditorBackend::setupHandlerAndStatements() +{ + AbstractView *view = m_view; + QTC_ASSERT(view, return); + + if (m_source.isEmpty()) { + m_actionType = StatementDelegate::Custom; + m_handler = ScriptEditorStatements::EmptyBlock(); + } else { + m_handler = ScriptEditorEvaluator::parseStatement(m_source); + const QString statementType = QmlDesigner::ScriptEditorStatements::toDisplayName(m_handler); + if (statementType == ScriptEditorStatements::EMPTY_DISPLAY_NAME) { + m_actionType = StatementDelegate::Custom; + } else if (statementType == ScriptEditorStatements::ASSIGNMENT_DISPLAY_NAME) { + m_actionType = StatementDelegate::Assign; + } else if (statementType == ScriptEditorStatements::SETPROPERTY_DISPLAY_NAME) { + m_actionType = StatementDelegate::SetProperty; + } else if (statementType == ScriptEditorStatements::FUNCTION_DISPLAY_NAME) { + m_actionType = StatementDelegate::CallFunction; + } else if (statementType == ScriptEditorStatements::SETSTATE_DISPLAY_NAME) { + m_actionType = StatementDelegate::ChangeState; + } else if (statementType == ScriptEditorStatements::LOG_DISPLAY_NAME) { + m_actionType = StatementDelegate::PrintMessage; + } else { + m_actionType = StatementDelegate::Custom; + } + } + + ScriptEditorStatements::MatchedStatement &okStatement = ScriptEditorStatements::okStatement( + m_handler); + m_okStatementDelegate.setStatement(okStatement); + m_okStatementDelegate.setActionType(m_actionType); + + ScriptEditorStatements::MatchedStatement &koStatement = ScriptEditorStatements::koStatement( + m_handler); + + if (!ScriptEditorStatements::isEmptyStatement(koStatement)) { + m_koStatementDelegate.setStatement(koStatement); + m_koStatementDelegate.setActionType(m_actionType); + } + + setHasElse(!ScriptEditorStatements::isEmptyStatement(koStatement)); + + emit actionTypeChanged(); +} + +void ScriptEditorBackend::handleOkStatementChanged() +{ + ScriptEditorStatements::MatchedStatement &okStatement = ScriptEditorStatements::okStatement( + m_handler); + + okStatement = m_okStatementDelegate.statement(); //TODO why? + + QString newSource = ScriptEditorStatements::toJavascript(m_handler); + + commitNewSource(newSource); +} + +void ScriptEditorBackend::handleKOStatementChanged() +{ + ScriptEditorStatements::MatchedStatement &koStatement = ScriptEditorStatements::koStatement( + m_handler); + + koStatement = m_koStatementDelegate.statement(); //TODO why? + + QString newSource = ScriptEditorStatements::toJavascript(m_handler); + + commitNewSource(newSource); +} + +void ScriptEditorBackend::handleConditionChanged() +{ + AbstractView *view = m_view; + + QTC_ASSERT(view, return); + QTC_ASSERT(view->isAttached(), return); + + ScriptEditorStatements::MatchedCondition &condition = ScriptEditorStatements::matchedCondition( + m_handler); + condition = m_conditionListModel.condition(); //why? + QString newSource = ScriptEditorStatements::toJavascript(m_handler); + + commitNewSource(newSource); +} + +void ScriptEditorBackend::setPropertySource(const QString &source) +{ + auto property = getSourceProperty(); + + QTC_ASSERT(property.isValid(), return); + + if (source.isEmpty()) + return; + + auto normalizedSource = QmlDesigner::SignalHandlerProperty::normalizedSourceWithBraces(source); + if (property.exists()) + property.toBindingProperty().setExpression(normalizedSource); + else + property.parentModelNode().bindingProperty(property.name()).setExpression(normalizedSource); +} + +void ScriptEditorBackend::commitNewSource(const QString &source) +{ + AbstractView *view = m_view; + + QTC_ASSERT(view, return); + QTC_ASSERT(view->isAttached(), return); + + m_blockReflection = true; + view->executeInTransaction("ScriptEditorBackend::commitNewSource", + [&]() { setPropertySource(source); }); + setSource(getSourceFromProperty(getSourceProperty())); + m_blockReflection = false; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/scripteditor/scripteditorbackend.h b/src/plugins/qmldesigner/components/scripteditor/scripteditorbackend.h new file mode 100644 index 00000000000..496c4d198a0 --- /dev/null +++ b/src/plugins/qmldesigner/components/scripteditor/scripteditorbackend.h @@ -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 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 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 m_view = nullptr; +}; +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp b/src/plugins/qmldesigner/components/scripteditor/scripteditorevaluator.cpp similarity index 80% rename from src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp rename to src/plugins/qmldesigner/components/scripteditor/scripteditorevaluator.cpp index 7bea5cd2cdd..2f6d8ba6c2f 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp +++ b/src/plugins/qmldesigner/components/scripteditor/scripteditorevaluator.cpp @@ -1,10 +1,10 @@ // 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 "connectioneditorevaluator.h" -#include "connectioneditorutils.h" +#include "scripteditorevaluator.h" #include "qmljs/parser/qmljsast_p.h" #include "qmljs/qmljsdocument.h" +#include "scripteditorutils.h" #include @@ -14,10 +14,10 @@ using namespace QmlDesigner; using QmlJS::AST::Node; using Kind = Node::Kind; -using ConnectionEditorStatements::ConditionalStatement; -using ConnectionEditorStatements::ConditionToken; -using ConnectionEditorStatements::MatchedCondition; -using ConnectionEditorStatements::MatchedStatement; +using ScriptEditorStatements::ConditionalStatement; +using ScriptEditorStatements::ConditionToken; +using ScriptEditorStatements::MatchedCondition; +using ScriptEditorStatements::MatchedStatement; namespace { enum class TrackingArea { No, Condition, Ok, Ko }; @@ -255,14 +255,14 @@ protected: void throwRecursionDepthError() override { checkValidityAndReturn(false, "Recursion depth problem"); - qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Recursion depth error"; + qCWarning(ScriptEditorLog) << __FUNCTION__ << "Recursion depth error"; } void checkAndResetVariable() { if (--m_depth == 0) { m_condition.statements.push_back( - ConnectionEditorStatements::Variable{m_identifier, m_fields.join(".")}); + ScriptEditorStatements::Variable{m_identifier, m_fields.join(".")}); m_identifier.clear(); m_fields.clear(); } @@ -300,7 +300,7 @@ private: class RightHandVisitor : public QmlJS::AST::Visitor { public: - ConnectionEditorStatements::RightHandSide rhs() const { return m_rhs; } + ScriptEditorStatements::RightHandSide rhs() const { return m_rhs; } void reset() { @@ -318,48 +318,48 @@ public: if (!isValid()) return false; - return ConnectionEditorStatements::isLiteralType(rhs()); + return ScriptEditorStatements::isLiteralType(rhs()); } bool couldBeLHS() const { if (!isValid()) return false; - return std::holds_alternative(m_rhs); + return std::holds_alternative(m_rhs); } inline bool couldBeVariable() const { return couldBeLHS(); } - ConnectionEditorStatements::Literal literal() const + ScriptEditorStatements::Literal literal() const { if (!isLiteralType()) return {}; return std::visit( - Overload{[](const bool &var) -> ConnectionEditorStatements::Literal { return var; }, - [](const double &var) -> ConnectionEditorStatements::Literal { return var; }, - [](const QString &var) -> ConnectionEditorStatements::Literal { return var; }, - [](const auto &) -> ConnectionEditorStatements::Literal { return false; }}, + Overload{[](const bool &var) -> ScriptEditorStatements::Literal { return var; }, + [](const double &var) -> ScriptEditorStatements::Literal { return var; }, + [](const QString &var) -> ScriptEditorStatements::Literal { return var; }, + [](const auto &) -> ScriptEditorStatements::Literal { return false; }}, m_rhs); } - ConnectionEditorStatements::Variable lhs() const + ScriptEditorStatements::Variable lhs() const { if (!isValid()) return {}; - if (auto rhs = std::get_if(&m_rhs)) + if (auto rhs = std::get_if(&m_rhs)) return *rhs; return {}; } - ConnectionEditorStatements::Variable variable() const + ScriptEditorStatements::Variable variable() const { if (!isValid()) return {}; - if (auto rhs = std::get_if(&m_rhs)) + if (auto rhs = std::get_if(&m_rhs)) return *rhs; return {}; @@ -478,13 +478,13 @@ protected: void throwRecursionDepthError() override { setFailed(); - qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Recursion depth error"; + qCWarning(ScriptEditorLog) << __FUNCTION__ << "Recursion depth error"; } void checkAndResetCal() { 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_identifier.clear(); @@ -495,7 +495,7 @@ protected: void checkAndResetNonCal() { 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_identifier.clear(); @@ -513,22 +513,23 @@ private: int m_depth = 0; QString m_identifier; QStringList m_fields; - ConnectionEditorStatements::RightHandSide m_rhs; + ScriptEditorStatements::RightHandSide m_rhs; }; MatchedStatement checkForStateSet(const MatchedStatement ¤tState) { - using namespace ConnectionEditorStatements; - return std::visit( - Overload{[](const PropertySet &propertySet) -> MatchedStatement { - if (propertySet.lhs.nodeId.size() && propertySet.lhs.propertyName == u"state" - && std::holds_alternative(propertySet.rhs)) - return StateSet{propertySet.lhs.nodeId, - ConnectionEditorStatements::toString(propertySet.rhs)}; - return propertySet; - }, - [](const auto &pSet) -> MatchedStatement { return pSet; }}, - currentState); + using namespace ScriptEditorStatements; + return std::visit(Overload{[](const PropertySet &propertySet) -> MatchedStatement { + if (propertySet.lhs.nodeId.size() + && propertySet.lhs.propertyName == u"state" + && std::holds_alternative(propertySet.rhs)) + return StateSet{propertySet.lhs.nodeId, + ScriptEditorStatements::toString( + propertySet.rhs)}; + return propertySet; + }, + [](const auto &pSet) -> MatchedStatement { return pSet; }}, + currentState); } class ConsoleLogEvaluator : public QmlJS::AST::Visitor @@ -536,9 +537,9 @@ class ConsoleLogEvaluator : public QmlJS::AST::Visitor public: bool isValid() { return m_completed; } - ConnectionEditorStatements::ConsoleLog expression() + ScriptEditorStatements::ConsoleLog expression() { - return ConnectionEditorStatements::ConsoleLog{m_arg}; + return ScriptEditorStatements::ConsoleLog{m_arg}; } protected: @@ -618,14 +619,14 @@ protected: void throwRecursionDepthError() override { m_failed = true; - qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Recursion depth error"; + qCWarning(ScriptEditorLog) << __FUNCTION__ << "Recursion depth error"; } private: bool m_failed = false; bool m_completed = false; int m_stepId = 0; - ConnectionEditorStatements::RightHandSide m_arg; + ScriptEditorStatements::RightHandSide m_arg; }; struct StatementReply @@ -650,10 +651,10 @@ struct StatementReply } // namespace -class QmlDesigner::ConnectionEditorEvaluatorPrivate +class QmlDesigner::ScriptEditorEvaluatorPrivate { - friend class ConnectionEditorEvaluator; - using Status = ConnectionEditorEvaluator::Status; + friend class ScriptEditorEvaluator; + using Status = ScriptEditorEvaluator::Status; public: bool checkValidityAndReturn(bool valid, const QString &parseError = {}); @@ -731,28 +732,28 @@ private: QString m_errorString; Status m_checkStatus = Status::UnStarted; QList m_nodeHierarchy; - ConnectionEditorStatements::Handler m_handler; + ScriptEditorStatements::Handler m_handler; }; -ConnectionEditorEvaluator::ConnectionEditorEvaluator() - : d(std::make_unique()) +ScriptEditorEvaluator::ScriptEditorEvaluator() + : d(std::make_unique()) {} -ConnectionEditorEvaluator::~ConnectionEditorEvaluator() {} +ScriptEditorEvaluator::~ScriptEditorEvaluator() {} -ConnectionEditorEvaluator::Status ConnectionEditorEvaluator::status() const +ScriptEditorEvaluator::Status ScriptEditorEvaluator::status() const { 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::Dialect::JavaScript); @@ -761,23 +762,23 @@ QString ConnectionEditorEvaluator::getDisplayStringForType(const QString &statem newDoc->parseJavaScript(); if (!newDoc->isParsedCorrectly()) - return ConnectionEditorStatements::CUSTOM_DISPLAY_NAME; + return ScriptEditorStatements::CUSTOM_DISPLAY_NAME; newDoc->ast()->accept(&evaluator); - const bool valid = evaluator.status() == ConnectionEditorEvaluator::Succeeded; + const bool valid = evaluator.status() == ScriptEditorEvaluator::Succeeded; if (!valid) - return ConnectionEditorStatements::CUSTOM_DISPLAY_NAME; + return ScriptEditorStatements::CUSTOM_DISPLAY_NAME; 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::Dialect::JavaScript); @@ -786,19 +787,19 @@ ConnectionEditorStatements::Handler ConnectionEditorEvaluator::parseStatement(co newDoc->parseJavaScript(); if (!newDoc->isParsedCorrectly()) - return ConnectionEditorStatements::EmptyBlock{}; + return ScriptEditorStatements::EmptyBlock{}; newDoc->ast()->accept(&evaluator); - const bool valid = evaluator.status() == ConnectionEditorEvaluator::Succeeded; + const bool valid = evaluator.status() == ScriptEditorEvaluator::Succeeded; if (!valid) - return ConnectionEditorStatements::EmptyBlock{}; + return ScriptEditorStatements::EmptyBlock{}; return evaluator.resultNode(); } -bool ConnectionEditorEvaluator::preVisit(Node *node) +bool ScriptEditorEvaluator::preVisit(Node *node) { if (d->m_nodeHierarchy.size()) { 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()) { 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->setTrackingArea(false, 0); d->m_ifStatement = 0; d->m_consoleLogCount = 0; d->m_consoleIdentifierCount = 0; - d->m_handler = ConnectionEditorStatements::EmptyBlock{}; + d->m_handler = ScriptEditorStatements::EmptyBlock{}; return true; } -bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::IfStatement *ifStatement) +bool ScriptEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::IfStatement *ifStatement) { if (d->m_ifStatement++) 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); } -bool ConnectionEditorEvaluator::visit(QmlJS::AST::IdentifierExpression *identifier) +bool ScriptEditorEvaluator::visit(QmlJS::AST::IdentifierExpression *identifier) { if (d->parentNodeStatus() == Kind::Kind_FieldMemberExpression) if (d->m_consoleLogCount) @@ -896,7 +897,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::IdentifierExpression *identifi return d->checkValidityAndReturn(true); } -bool ConnectionEditorEvaluator::visit(QmlJS::AST::BinaryExpression *binaryExpression) +bool ScriptEditorEvaluator::visit(QmlJS::AST::BinaryExpression *binaryExpression) { if (d->isInIfCondition()) { if (binaryExpression->op == QSOperator::Assign) @@ -923,7 +924,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::BinaryExpression *binaryExpres return false; } else { MatchedStatement *currentStatement = d->currentStatement(); - if (currentStatement && ConnectionEditorStatements::isEmptyStatement(*currentStatement) + if (currentStatement && ScriptEditorStatements::isEmptyStatement(*currentStatement) && d->parentNodeStatus().childId() == 0) { if (binaryExpression->op == QSOperator::Assign) { RightHandVisitor variableVisitor; @@ -932,16 +933,16 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::BinaryExpression *binaryExpres if (!variableVisitor.couldBeLHS()) return d->checkValidityAndReturn(false, "Invalid left hand."); - ConnectionEditorStatements::Variable lhs = variableVisitor.lhs(); + ScriptEditorStatements::Variable lhs = variableVisitor.lhs(); variableVisitor.reset(); binaryExpression->right->accept(&variableVisitor); if (variableVisitor.couldBeLHS()) { - ConnectionEditorStatements::Assignment assignment{lhs, variableVisitor.variable()}; + ScriptEditorStatements::Assignment assignment{lhs, variableVisitor.variable()}; *currentStatement = assignment; } else if (variableVisitor.isLiteralType()) { - ConnectionEditorStatements::PropertySet propSet{lhs, variableVisitor.literal()}; + ScriptEditorStatements::PropertySet propSet{lhs, variableVisitor.literal()}; *currentStatement = propSet; } else { return d->checkValidityAndReturn(false, "Invalid RHS"); @@ -955,7 +956,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::BinaryExpression *binaryExpres 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") d->m_consoleLogCount++; @@ -965,7 +966,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::FieldMemberExpression *fieldEx return d->checkValidityAndReturn(true); } -bool ConnectionEditorEvaluator::visit(QmlJS::AST::CallExpression *callExpression) +bool ScriptEditorEvaluator::visit(QmlJS::AST::CallExpression *callExpression) { if (d->isInIfCondition()) return d->checkValidityAndReturn(false, "Functions are not allowd in the expressions"); @@ -974,7 +975,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::CallExpression *callExpression if (!currentStatement) return d->checkValidityAndReturn(false, "Invalid place to call an expression"); - if (ConnectionEditorStatements::isEmptyStatement(*currentStatement)) { + if (ScriptEditorStatements::isEmptyStatement(*currentStatement)) { if (d->parentNodeStatus().childId() == 0) { ConsoleLogEvaluator logEvaluator; callExpression->accept(&logEvaluator); @@ -985,8 +986,8 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::CallExpression *callExpression callExpression->accept(&callVisitor); if (callVisitor.isValid()) { - ConnectionEditorStatements::RightHandSide rhs = callVisitor.rhs(); - if (auto rhs_ = std::get_if(&rhs)) + ScriptEditorStatements::RightHandSide rhs = callVisitor.rhs(); + if (auto rhs_ = std::get_if(&rhs)) *currentStatement = *rhs_; else return d->checkValidityAndReturn(false, "Invalid Matched Function type."); @@ -999,7 +1000,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::CallExpression *callExpression 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(); @@ -1013,7 +1014,7 @@ bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::Block *block) 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) return d->checkValidityAndReturn(false, "Arguments are not supported in if condition"); @@ -1022,7 +1023,7 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::ArgumentList *arguments) if (!currentStatement) 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"); 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"); } -void ConnectionEditorEvaluator::endVisit([[maybe_unused]] QmlJS::AST::Program *program) +void ScriptEditorEvaluator::endVisit([[maybe_unused]] QmlJS::AST::Program *program) { if (status() == UnFinished) d->setStatus(Succeeded); } -void ConnectionEditorEvaluator::endVisit(QmlJS::AST::FieldMemberExpression *fieldExpression) +void ScriptEditorEvaluator::endVisit(QmlJS::AST::FieldMemberExpression *fieldExpression) { if (status() != UnFinished) 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; } -void ConnectionEditorEvaluator::endVisit(QmlJS::AST::IfStatement * /*ifStatement*/) +void ScriptEditorEvaluator::endVisit(QmlJS::AST::IfStatement * /*ifStatement*/) { if (status() != UnFinished) 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) return; @@ -1080,26 +1081,26 @@ void ConnectionEditorEvaluator::endVisit(QmlJS::AST::StatementList * /*statement d->checkValidityAndReturn(false, "More than one statements are available."); } -void ConnectionEditorEvaluator::throwRecursionDepthError() +void ScriptEditorEvaluator::throwRecursionDepthError() { 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 (m_checkStatus != Status::Failed) { setStatus(Status::Failed); m_errorString = parseError; - qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Parse error" << parseError; + qCWarning(ScriptEditorLog) << __FUNCTION__ << "Parse error" << parseError; } } return m_checkStatus; } -NodeStatus ConnectionEditorEvaluatorPrivate::nodeStatus(int reverseLevel) const +NodeStatus ScriptEditorEvaluatorPrivate::nodeStatus(int reverseLevel) const { if (m_nodeHierarchy.size() > reverseLevel) return m_nodeHierarchy.at(m_nodeHierarchy.size() - reverseLevel - 1); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.h b/src/plugins/qmldesigner/components/scripteditor/scripteditorevaluator.h similarity index 77% rename from src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.h rename to src/plugins/qmldesigner/components/scripteditor/scripteditorevaluator.h index 2cc285ed6c5..33cae4b7e6d 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.h +++ b/src/plugins/qmldesigner/components/scripteditor/scripteditorevaluator.h @@ -3,7 +3,7 @@ #pragma once -#include "connectioneditorstatements.h" +#include "scripteditorstatements.h" #include "qmldesigner_global.h" @@ -11,21 +11,21 @@ namespace QmlDesigner { -class ConnectionEditorEvaluatorPrivate; +class ScriptEditorEvaluatorPrivate; -class QMLDESIGNER_EXPORT ConnectionEditorEvaluator : public QmlJS::AST::Visitor +class QMLDESIGNER_EXPORT ScriptEditorEvaluator : public QmlJS::AST::Visitor { public: enum Status { UnStarted, UnFinished, Succeeded, Failed }; - ConnectionEditorEvaluator(); - virtual ~ConnectionEditorEvaluator(); + ScriptEditorEvaluator(); + virtual ~ScriptEditorEvaluator(); Status status() const; - ConnectionEditorStatements::Handler resultNode() const; + ScriptEditorStatements::Handler resultNode() const; static QString getDisplayStringForType(const QString &statement); - static ConnectionEditorStatements::Handler parseStatement(const QString &statement); + static ScriptEditorStatements::Handler parseStatement(const QString &statement); protected: bool preVisit(QmlJS::AST::Node *node) override; @@ -49,7 +49,7 @@ protected: void throwRecursionDepthError() override; private: - std::unique_ptr d; + std::unique_ptr d; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp b/src/plugins/qmldesigner/components/scripteditor/scripteditorstatements.cpp similarity index 71% rename from src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp rename to src/plugins/qmldesigner/components/scripteditor/scripteditorstatements.cpp index 5fbad9acadb..cc8c2872b98 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp +++ b/src/plugins/qmldesigner/components/scripteditor/scripteditorstatements.cpp @@ -1,11 +1,11 @@ // 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 "connectioneditorstatements.h" +#include "scripteditorstatements.h" #include using namespace QmlDesigner; -using namespace ConnectionEditorStatements; +using namespace ScriptEditorStatements; namespace { template @@ -39,31 +39,31 @@ struct StringVisitor return "Variable{" + var.nodeId + propertyName + "}"; } - QString operator()(const ConnectionEditorStatements::MatchedFunction &func) + QString operator()(const ScriptEditorStatements::MatchedFunction &func) { 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) + "}"; } - QString operator()(const ConnectionEditorStatements::PropertySet &propertySet) + QString operator()(const ScriptEditorStatements::PropertySet &propertySet) { return "PropertySet{" + propertySet.lhs.expression() + " = " + std::visit(StringVisitor{}, propertySet.rhs) + "}"; } - QString operator()(const ConnectionEditorStatements::StateSet &stateSet) + QString operator()(const ScriptEditorStatements::StateSet &stateSet) { 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) + "}"; } @@ -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()) return "MatchedCondition{}"; @@ -113,7 +113,7 @@ struct StringVisitor return value; } - QString operator()(const ConnectionEditorStatements::ConditionalStatement &conditional) + QString operator()(const ScriptEditorStatements::ConditionalStatement &conditional) { QString value; value.reserve(200); @@ -130,7 +130,7 @@ struct StringVisitor return value; } - QString operator()(const ConnectionEditorStatements::MatchedStatement &conditional) + QString operator()(const ScriptEditorStatements::MatchedStatement &conditional) { return std::visit(StringVisitor{}, conditional); } @@ -156,7 +156,7 @@ struct JSOverload return var.nodeId + propertyName; } - QString operator()(const ConnectionEditorStatements::MatchedFunction &func) + QString operator()(const ScriptEditorStatements::MatchedFunction &func) { QString funcName; if (func.functionName.size()) @@ -165,31 +165,31 @@ struct JSOverload return func.nodeId + funcName + "()"; } - QString operator()(const ConnectionEditorStatements::Assignment &assignment) + QString operator()(const ScriptEditorStatements::Assignment &assignment) { 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); } - QString operator()(const ConnectionEditorStatements::StateSet &stateSet) + QString operator()(const ScriptEditorStatements::StateSet &stateSet) { 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) + ")"; } 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()) return {}; @@ -209,7 +209,7 @@ struct JSOverload return value; } - QString operator()(const ConnectionEditorStatements::MatchedStatement &statement) + QString operator()(const ScriptEditorStatements::MatchedStatement &statement) { if (isEmptyStatement(statement)) return {}; @@ -217,7 +217,7 @@ struct JSOverload return std::visit(JSOverload{}, statement); } - QString operator()(const ConnectionEditorStatements::ConditionalStatement &conditional) + QString operator()(const ScriptEditorStatements::ConditionalStatement &conditional) { QString value; value.reserve(200); @@ -240,47 +240,47 @@ struct JSOverload } // namespace -bool ConnectionEditorStatements::isEmptyStatement(const MatchedStatement &stat) +bool ScriptEditorStatements::isEmptyStatement(const MatchedStatement &stat) { return std::holds_alternative(stat); } -QString ConnectionEditorStatements::toString(const ComparativeStatement &stat) +QString ScriptEditorStatements::toString(const ComparativeStatement &stat) { return std::visit(StringVisitor{}, stat); } -QString ConnectionEditorStatements::toString(const RightHandSide &rhs) +QString ScriptEditorStatements::toString(const RightHandSide &rhs) { return std::visit(StringVisitor{}, rhs); } -QString ConnectionEditorStatements::toString(const Literal &literal) +QString ScriptEditorStatements::toString(const Literal &literal) { return std::visit(StringVisitor{}, literal); } -QString ConnectionEditorStatements::toString(const MatchedStatement &statement) +QString ScriptEditorStatements::toString(const MatchedStatement &statement) { return std::visit(StringVisitor{}, statement); } -QString ConnectionEditorStatements::toString(const Handler &handler) +QString ScriptEditorStatements::toString(const Handler &handler) { return std::visit(StringVisitor{}, handler); } -QString ConnectionEditorStatements::toJavascript(const Handler &handler) +QString ScriptEditorStatements::toJavascript(const Handler &handler) { return std::visit(JSOverload{}, handler); } -bool ConnectionEditorStatements::isConsoleLog(const MatchedStatement &curState) +bool ScriptEditorStatements::isConsoleLog(const MatchedStatement &curState) { return std::holds_alternative(curState); } -bool ConnectionEditorStatements::isLiteralType(const RightHandSide &var) +bool ScriptEditorStatements::isLiteralType(const RightHandSide &var) { return std::visit(Overload{[](const double &) { return true; }, [](const bool &) { return true; }, @@ -289,7 +289,7 @@ bool ConnectionEditorStatements::isLiteralType(const RightHandSide &var) var); } -QString ConnectionEditorStatements::toDisplayName(const MatchedStatement &statement) +QString ScriptEditorStatements::toDisplayName(const MatchedStatement &statement) { const char *displayName = std::visit( Overload{[](const MatchedFunction &) { return FUNCTION_DISPLAY_NAME; }, @@ -303,7 +303,7 @@ QString ConnectionEditorStatements::toDisplayName(const MatchedStatement &statem return QString::fromLatin1(displayName); } -QString ConnectionEditorStatements::toDisplayName(const Handler &handler) +QString ScriptEditorStatements::toDisplayName(const Handler &handler) { const MatchedStatement &statement = std::visit( Overload{[](const MatchedStatement &statement) { return statement; }, @@ -312,51 +312,49 @@ QString ConnectionEditorStatements::toDisplayName(const Handler &handler) return toDisplayName(statement); } -MatchedStatement &ConnectionEditorStatements::okStatement( - ConnectionEditorStatements::Handler &handler) +MatchedStatement &ScriptEditorStatements::okStatement(ScriptEditorStatements::Handler &handler) { MatchedStatement statement; - return std::visit(Overload{[](ConnectionEditorStatements::MatchedStatement &var) - -> MatchedStatement & { return var; }, - [](ConnectionEditorStatements::ConditionalStatement &statement) + return std::visit(Overload{[](ScriptEditorStatements::MatchedStatement &var) -> MatchedStatement & { + return var; + }, + [](ScriptEditorStatements::ConditionalStatement &statement) -> MatchedStatement & { return statement.ok; }}, handler); } -MatchedStatement &ConnectionEditorStatements::koStatement( - ConnectionEditorStatements::Handler &handler) +MatchedStatement &ScriptEditorStatements::koStatement(ScriptEditorStatements::Handler &handler) { static MatchedStatement block; - if (auto *statement = std::get_if(&handler)) + if (auto *statement = std::get_if(&handler)) return statement->ko; return block; } -MatchedCondition &ConnectionEditorStatements::matchedCondition(Handler &handler) +MatchedCondition &ScriptEditorStatements::matchedCondition(Handler &handler) { static MatchedCondition block; - if (auto *statement = std::get_if(&handler)) + if (auto *statement = std::get_if(&handler)) return statement->condition; return block; } -ConditionalStatement &ConnectionEditorStatements::conditionalStatement( - ConnectionEditorStatements::Handler &handler) +ConditionalStatement &ScriptEditorStatements::conditionalStatement(ScriptEditorStatements::Handler &handler) { static ConditionalStatement block; - if (auto *statement = std::get_if(&handler)) + if (auto *statement = std::get_if(&handler)) return *statement; return block; } -QString ConnectionEditorStatements::toJavascript(const ConditionToken &token) +QString ScriptEditorStatements::toJavascript(const ConditionToken &token) { switch (token) { case ConditionToken::Not: diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h b/src/plugins/qmldesigner/components/scripteditor/scripteditorstatements.h similarity index 77% rename from src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h rename to src/plugins/qmldesigner/components/scripteditor/scripteditorstatements.h index 06e6434b5ab..34f046b117f 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h +++ b/src/plugins/qmldesigner/components/scripteditor/scripteditorstatements.h @@ -7,22 +7,22 @@ #include namespace QmlDesigner { -namespace ConnectionEditorStatements { +namespace ScriptEditorStatements { inline constexpr char FUNCTION_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( - "QmlDesigner::ConnectionEditorStatements", "Function"); + "QmlDesigner::ScriptEditorStatements", "Function"); inline constexpr char ASSIGNMENT_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( - "QmlDesigner::ConnectionEditorStatements", "Assignment"); + "QmlDesigner::ScriptEditorStatements", "Assignment"); 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( - "QmlDesigner::ConnectionEditorStatements", "Set State"); -inline constexpr char LOG_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( - "QmlDesigner::ConnectionEditorStatements", "Print"); + "QmlDesigner::ScriptEditorStatements", "Set State"); +inline constexpr char LOG_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QmlDesigner::ScriptEditorStatements", + "Print"); inline constexpr char EMPTY_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( - "QmlDesigner::ConnectionEditorStatements", "Empty"); + "QmlDesigner::ScriptEditorStatements", "Empty"); inline constexpr char CUSTOM_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( - "QmlDesigner::ConnectionEditorStatements", "Custom"); + "QmlDesigner::ScriptEditorStatements", "Custom"); struct Variable; struct MatchedFunction; @@ -122,13 +122,12 @@ QMLDESIGNER_EXPORT QString toDisplayName(const MatchedStatement &statement); QMLDESIGNER_EXPORT QString toDisplayName(const Handler &handler); QMLDESIGNER_EXPORT QString toJavascript(const ConditionToken &token); -QMLDESIGNER_EXPORT MatchedStatement &okStatement(ConnectionEditorStatements::Handler &handler); -QMLDESIGNER_EXPORT MatchedStatement &koStatement(ConnectionEditorStatements::Handler &handler); +QMLDESIGNER_EXPORT MatchedStatement &okStatement(ScriptEditorStatements::Handler &handler); +QMLDESIGNER_EXPORT MatchedStatement &koStatement(ScriptEditorStatements::Handler &handler); -QMLDESIGNER_EXPORT MatchedCondition &matchedCondition(ConnectionEditorStatements::Handler &handler); -QMLDESIGNER_EXPORT ConditionalStatement &conditionalStatement( - ConnectionEditorStatements::Handler &handler); +QMLDESIGNER_EXPORT MatchedCondition &matchedCondition(ScriptEditorStatements::Handler &handler); +QMLDESIGNER_EXPORT ConditionalStatement &conditionalStatement(ScriptEditorStatements::Handler &handler); -} // namespace ConnectionEditorStatements +} // namespace ScriptEditorStatements } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp b/src/plugins/qmldesigner/components/scripteditor/scripteditorutils.cpp similarity index 92% rename from src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp rename to src/plugins/qmldesigner/components/scripteditor/scripteditorutils.cpp index fcedbc9876c..36c5ed54d7c 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/scripteditor/scripteditorutils.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 "connectioneditorutils.h" +#include "scripteditorutils.h" #include #include @@ -8,11 +8,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include @@ -24,7 +24,7 @@ namespace QmlDesigner { -Q_LOGGING_CATEGORY(ConnectionEditorLog, "qtc.qtquickdesigner.connectioneditor", QtWarningMsg) +Q_LOGGING_CATEGORY(ScriptEditorLog, "qtc.qtquickdesigner.scripteditor", QtWarningMsg) void callLater(const std::function &fun) { @@ -62,7 +62,7 @@ PropertyName uniquePropertyName(const PropertyName &suggestion, const ModelNode 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. + // only types we care about in the script editor. // TODO: Support all possible AbstractProperty types and move to the // AbstractProperty class. if (typeName == "bool") @@ -80,7 +80,7 @@ NodeMetaInfo dynamicTypeNameToNodeMetaInfo(const TypeName &typeName, Model *mode else if (typeName == "var" || typeName == "variant") return model->metaInfo("QML.variant"); else - qCWarning(ConnectionEditorLog) << __FUNCTION__ << "type" << typeName << "not found"; + qCWarning(ScriptEditorLog) << __FUNCTION__ << "type" << typeName << "not found"; return {}; } @@ -355,7 +355,7 @@ QStringList availableTargetProperties(const BindingProperty &bindingProperty) { const ModelNode modelNode = bindingProperty.parentModelNode(); if (!modelNode.isValid()) { - qCWarning(ConnectionEditorLog) << __FUNCTION__ << "invalid model node"; + qCWarning(ScriptEditorLog) << __FUNCTION__ << "invalid model node"; return {}; } @@ -428,7 +428,7 @@ QStringList availableSourceProperties(const QString &id, } else if (auto metaInfo = targetProperty.parentModelNode().metaInfo(); metaInfo.isValid()) { targetType = metaInfo.property(targetProperty.name()).propertyType(); } else - qCWarning(ConnectionEditorLog) << __FUNCTION__ << "no meta info for target node"; + qCWarning(ScriptEditorLog) << __FUNCTION__ << "no meta info for target node"; QStringList possibleProperties; if (!modelNode.isValid()) { @@ -450,7 +450,7 @@ QStringList availableSourceProperties(const QString &id, return possibleProperties; } #endif - qCWarning(ConnectionEditorLog) << __FUNCTION__ << "invalid model node:" << id; + qCWarning(ScriptEditorLog) << __FUNCTION__ << "invalid model node:" << id; return {}; } @@ -476,10 +476,40 @@ QStringList availableSourceProperties(const QString &id, possibleProperties.push_back(QString::fromUtf8(property.name())); } } else { - qCWarning(ConnectionEditorLog) << __FUNCTION__ << "no meta info for source node"; + qCWarning(ScriptEditorLog) << __FUNCTION__ << "no meta info for source node"; } 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 diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h b/src/plugins/qmldesigner/components/scripteditor/scripteditorutils.h similarity index 93% rename from src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h rename to src/plugins/qmldesigner/components/scripteditor/scripteditorutils.h index 220fdc9e11d..9d5d170381c 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h +++ b/src/plugins/qmldesigner/components/scripteditor/scripteditorutils.h @@ -13,7 +13,7 @@ namespace QmlDesigner { -Q_DECLARE_LOGGING_CATEGORY(ConnectionEditorLog) +Q_DECLARE_LOGGING_CATEGORY(ScriptEditorLog) class AbstractView; class AbstractProperty; @@ -26,6 +26,8 @@ void showErrorMessage(const QString &text); QString idOrTypeName(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 dynamicTypeNameToNodeMetaInfo(const TypeName &typeName, Model *model);