// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Controls
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
import ScriptEditorBackend
Column {
id: root
readonly property real horizontalSpacing: 10
readonly property real verticalSpacing: 12
readonly property real columnWidth: (root.width - root.horizontalSpacing) / 2
property var backend
property bool keepOpen: expressionDialogLoader.visible
property Window parentWindow: null
width: parent.width
spacing: root.verticalSpacing
TapHandler {
onTapped: root.forceActiveFocus()
}
Row {
spacing: root.horizontalSpacing
PopupLabel {
width: root.columnWidth
text: qsTr("Signal")
tooltip: qsTr("Sets an interaction method that connects to the Target component.")
}
PopupLabel {
width: root.columnWidth
text: qsTr("Action")
tooltip: qsTr("Sets an action that is associated with the selected Target component's Signal.")
}
}
Row {
spacing: root.horizontalSpacing
StudioControls.TopLevelComboBox {
id: signal
style: StudioTheme.Values.connectionPopupControlStyle
width: root.columnWidth
model: backend.signal.name.model ?? 0
onActivated: backend.signal.name.activateIndex(signal.currentIndex)
property int currentTypeIndex: backend.signal.name.currentIndex ?? 0
onCurrentTypeIndexChanged: signal.currentIndex = signal.currentTypeIndex
}
StudioControls.TopLevelComboBox {
id: action
style: StudioTheme.Values.connectionPopupControlStyle
width: root.columnWidth
textRole: "text"
valueRole: "value"
///model.getData(currentIndex, "role")
property int indexFromBackend: indexOfValue(backend.actionType)
onIndexFromBackendChanged: action.currentIndex = action.indexFromBackend
onActivated: backend.changeActionType(action.currentValue)
model: ListModel {
ListElement {
value: StatementDelegate.CallFunction
text: qsTr("Call Function")
enabled: true
}
ListElement {
value: StatementDelegate.Assign
text: qsTr("Assign")
enabled: true
}
ListElement {
value: StatementDelegate.ChangeState
text: qsTr("Change State")
enabled: true
}
ListElement {
value: StatementDelegate.SetProperty
text: qsTr("Set Property")
enabled: true
}
ListElement {
value: StatementDelegate.PrintMessage
text: qsTr("Print Message")
enabled: true
}
ListElement {
value: StatementDelegate.Custom
text: qsTr("Custom")
enabled: false
}
}
}
}
StatementEditor {
width: root.width
actionType: action.currentValue ?? StatementDelegate.Custom
horizontalSpacing: root.horizontalSpacing
columnWidth: root.columnWidth
statement: backend.okStatement
backend: root.backend
spacing: root.verticalSpacing
}
HelperWidgets.AbstractButton {
style: StudioTheme.Values.connectionPopupButtonStyle
width: 160
buttonIcon: qsTr("Add Condition")
tooltip: qsTr("Sets a logical condition for the selected Signal. It works with the properties of the Target component.")
iconSize: StudioTheme.Values.baseFontSize
iconFontFamily: StudioTheme.Constants.font.family
anchors.horizontalCenter: parent.horizontalCenter
visible: action.currentValue !== StatementDelegate.Custom && !backend.hasCondition
onClicked: backend.addCondition()
}
HelperWidgets.AbstractButton {
style: StudioTheme.Values.connectionPopupButtonStyle
width: 160
buttonIcon: qsTr("Remove Condition")
tooltip: qsTr("Removes the logical condition for the Target component.")
iconSize: StudioTheme.Values.baseFontSize
iconFontFamily: StudioTheme.Constants.font.family
anchors.horizontalCenter: parent.horizontalCenter
visible: action.currentValue !== StatementDelegate.Custom && backend.hasCondition
onClicked: backend.removeCondition()
}
ExpressionBuilder {
style: StudioTheme.Values.connectionPopupControlStyle
width: root.width
visible: backend.hasCondition
model: backend.conditionListModel
onRemove: function(index) {
//console.log("remove", index)
backend.conditionListModel.removeToken(index)
}
onUpdate: function(index, value) {
//console.log("update", index, value)
backend.conditionListModel.updateToken(index, value)
}
onAdd: function(value) {
//console.log("add", value)
backend.conditionListModel.appendToken(value)
}
onInsert: function(index, value, type) {
//console.log("insert", index, value, type)
if (type === ConditionListModel.Intermediate)
backend.conditionListModel.insertIntermediateToken(index, value)
else if (type === ConditionListModel.Shadow)
backend.conditionListModel.insertShadowToken(index, value)
else
backend.conditionListModel.insertToken(index, value)
}
onSetValue: function(index, value) {
//console.log("setValue", index, value)
backend.conditionListModel.setShadowToken(index, value)
}
}
HelperWidgets.AbstractButton {
style: StudioTheme.Values.connectionPopupButtonStyle
width: 160
buttonIcon: qsTr("Add Else Statement")
tooltip: qsTr("Sets an alternate condition for the previously defined logical condition.")
iconSize: StudioTheme.Values.baseFontSize
iconFontFamily: StudioTheme.Constants.font.family
anchors.horizontalCenter: parent.horizontalCenter
visible: action.currentValue !== StatementDelegate.Custom
&& backend.hasCondition && !backend.hasElse
onClicked: backend.addElse()
}
HelperWidgets.AbstractButton {
style: StudioTheme.Values.connectionPopupButtonStyle
width: 160
buttonIcon: qsTr("Remove Else Statement")
tooltip: qsTr("Removes the alternate logical condition for the previously defined logical condition.")
iconSize: StudioTheme.Values.baseFontSize
iconFontFamily: StudioTheme.Constants.font.family
anchors.horizontalCenter: parent.horizontalCenter
visible: action.currentValue !== StatementDelegate.Custom
&& backend.hasCondition && backend.hasElse
onClicked: backend.removeElse()
}
//Else Statement
StatementEditor {
width: root.width
actionType: action.currentValue ?? StatementDelegate.Custom
horizontalSpacing: root.horizontalSpacing
columnWidth: root.columnWidth
statement: backend.koStatement
backend: root.backend
spacing: root.verticalSpacing
visible: action.currentValue !== StatementDelegate.Custom
&& backend.hasCondition && backend.hasElse
}
// code preview toolbar
Column {
id: miniToolbarEditor
width: parent.width
spacing: -2
Rectangle {
id: miniToolbar
width: parent.width
height: editorButton.height + 2
radius: 4
z: -1
color: StudioTheme.Values.themeConnectionEditorMicroToolbar
Row {
spacing: 2
HelperWidgets.AbstractButton {
id: editorButton
style: StudioTheme.Values.microToolbarButtonStyle
buttonIcon: StudioTheme.Constants.codeEditor_medium
tooltip: qsTr("Write the conditions for the components and the signals manually.")
onClicked: expressionDialogLoader.show()
}
HelperWidgets.AbstractButton {
id: jumpToCodeButton
style: StudioTheme.Values.microToolbarButtonStyle
buttonIcon: StudioTheme.Constants.jumpToCode_medium
tooltip: qsTr("Jump to the code.")
onClicked: backend.jumpToCode()
}
}
}
// Editor
Rectangle {
id: editor
width: parent.width
height: 150
color: StudioTheme.Values.themeConnectionCodeEditor
Text {
id: code
anchors.fill: parent
anchors.margins: 4
text: backend.indentedSource
color: StudioTheme.Values.themeTextColor
font.pixelSize: StudioTheme.Values.myFontSize
wrapMode: Text.Wrap
horizontalAlignment: code.lineCount === 1 ? Text.AlignHCenter : Text.AlignLeft
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
Loader {
id: expressionDialogLoader
parent: editor
anchors.fill: parent
visible: false
active: visible
function show() {
expressionDialogLoader.visible = true
}
sourceComponent: Item {
id: bindingEditorParent
Component.onCompleted: {
bindingEditor.showWidget()
bindingEditor.text = backend.source
bindingEditor.showControls(false)
bindingEditor.setMultilne(true)
bindingEditor.updateWindowName()
}
ActionEditor {
id: bindingEditor
onRejected: {
bindingEditor.hideWidget()
expressionDialogLoader.visible = false
}
onAccepted: {
backend.setNewSource(bindingEditor.text)
bindingEditor.hideWidget()
expressionDialogLoader.visible = false
}
}
}
}
}
}
}