2022-10-18 21:24:58 +00:00
/ * *
* LogarithmPlotter - 2 D plotter software to make BODE plots , sequences and distribution functions .
2024-01-10 23:11:09 +00:00
* Copyright ( C ) 2021 - 2024 Ad5001
2022-10-18 21:24:58 +00:00
*
* This program is free software: you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < https: //www.gnu.org/licenses/>.
* /
2023-05-21 22:15:09 +00:00
import QtQuick . Controls
import QtQuick
import Qt . labs . platform as Native
2022-10-19 21:44:04 +00:00
import eu . ad5001 . LogarithmPlotter . Popup 1.0 as P
2022-10-18 21:24:58 +00:00
import "../js/mathlib.js" as MathLib
2022-10-18 23:16:54 +00:00
import "../js/utils.js" as Utils
2022-10-19 22:37:02 +00:00
import "../js/objects.js" as Objects
2022-10-18 23:16:54 +00:00
import "../js/parsing/parsing.js" as Parsing
2022-10-18 21:24:58 +00:00
/ * !
\ qmltype ExpressionEditor
\ inqmlmodule eu . ad5001 . LogarithmPlotter . Setting
\ brief Setting to edit strings and numbers .
2022-10-19 22:37:02 +00:00
\ sa EditorDialog , AutocompletionCategory
2022-10-18 21:24:58 +00:00
* /
Item {
id: control
height: 30
/ * !
\ qmlsignal ExpressionEditor: : changed ( var newValue )
Emitted when the value of the expression has been changed .
The corresponding handler is \ c onChanged .
* /
signal changed ( var newValue )
/ * !
\ qmlproperty string ExpressionEditor: : defValue
Default editable expression value of the editor .
* /
property string defValue
/ * !
\ qmlproperty string ExpressionEditor: : value
Value of the editor .
* /
property alias value: editor . text
/ * !
\ qmlproperty string ExpressionEditor: : self
Object or context of the expression to be edited .
Used to prevent circular dependency .
* /
property string self: ""
2022-10-19 23:14:09 +00:00
/ * !
\ qmlproperty var ExpressionEditor: : variables
Accepted variables for the expression .
* /
property var variables: [ ]
2022-10-18 21:24:58 +00:00
/ * !
\ qmlproperty string ExpressionEditor: : placeholderText
Value of the editor .
* /
property alias placeholderText: editor . placeholderText
/ * !
\ qmlproperty string ExpressionEditor: : label
Label of the editor .
* /
property string label
/ * !
\ qmlproperty string ExpressionEditor: : icon
Icon path of the editor .
* /
property string icon: ""
/ * !
\ qmlproperty string ExpressionEditor: : openAndCloseMatches
Characters that when pressed , should be immediately followed up by their closing character .
2022-10-18 23:16:54 +00:00
TODO: Make it configurable .
2022-10-18 21:24:58 +00:00
* /
readonly property var openAndCloseMatches: {
"(" : ")" ,
"[" : "]" ,
"'" : "'" ,
'"' : '"'
}
2022-10-18 23:16:54 +00:00
/ * !
2023-05-22 03:17:12 +00:00
\ qmlproperty string ExpressionEditor: : colorSchemes
Color schemes of the editor .
2022-10-18 23:16:54 +00:00
* /
2023-05-22 03:17:12 +00:00
readonly property var colorSchemes: [
{ // Breeze Light
'NORMAL' : "#1F1C1B" ,
'VARIABLE' : "#0057AE" ,
'CONSTANT' : "#006E28" ,
'FUNCTION' : "#644A9B" ,
'OPERATOR' : "#CA60CA" ,
'STRING' : "#BF0303" ,
'NUMBER' : "#B08000"
} ,
{ // Breeze Dark
'NORMAL' : "#CFCFC2" ,
'VARIABLE' : "#2980B9" ,
'CONSTANT' : "#27AE60" ,
'FUNCTION' : "#8E44AD" ,
'OPERATOR' : "#A44EA4" ,
'STRING' : "#F44F4F" ,
'NUMBER' : "#F67400"
} ,
{ // Solarized
'NORMAL' : "#839496" ,
'VARIABLE' : "#B58900" ,
'CONSTANT' : "#859900" ,
'FUNCTION' : "#268BD2" ,
'OPERATOR' : "#859900" ,
'STRING' : "#2AA198" ,
'NUMBER' : "#2AA198"
} ,
{ // GitHub Light
'NORMAL' : "#24292E" ,
'VARIABLE' : "#D73A49" ,
'CONSTANT' : "#6F42C1" ,
'FUNCTION' : "#6F42C1" ,
'OPERATOR' : "#24292E" ,
'STRING' : "#032F62" ,
'NUMBER' : "#005CC5"
} ,
{ // GitHub Dark
'NORMAL' : "#E1E4E8" ,
'VARIABLE' : "#F97583" ,
'CONSTANT' : "#B392f0" ,
'FUNCTION' : "#B392f0" ,
'OPERATOR' : "#E1E4E8" ,
'STRING' : "#9ECBFF" ,
'NUMBER' : "#79B8FF"
} ,
{ // Nord
'NORMAL' : "#D8DEE9" ,
'VARIABLE' : "#81A1C1" ,
'CONSTANT' : "#8FBCBB" ,
'FUNCTION' : "#88C0D0" ,
'OPERATOR' : "#81A1C1" ,
'STRING' : "#A3BE8C" ,
'NUMBER' : "#B48EAD"
} ,
{ // Monokai
'NORMAL' : "#F8F8F2" ,
'VARIABLE' : "#66D9EF" ,
'CONSTANT' : "#F92672" ,
'FUNCTION' : "#A6E22E" ,
'OPERATOR' : "#F8F8F2" ,
'STRING' : "#E6DB74" ,
'NUMBER' : "#AE81FF"
}
]
2022-10-18 23:16:54 +00:00
2022-10-18 21:24:58 +00:00
Icon {
id: iconLabel
anchors.top: parent . top
anchors.topMargin: icon == "" ? 0 : 3
source: control . visible && icon != "" ? "../icons/" + control.icon : ""
width: height
height: icon == "" || ! visible ? 0 : 24
color: sysPalette . windowText
}
Label {
id: labelItem
anchors.left: iconLabel . right
anchors.leftMargin: icon == "" ? 0 : 5
height: parent . height
anchors.top: parent . top
verticalAlignment: TextInput . AlignVCenter
//color: sysPalette.windowText
text: visible ? qsTranslate ( "control" , "%1: " ) . arg ( control . label ) : ""
visible: control . label != ""
}
2023-05-21 22:15:09 +00:00
Native . MessageDialog {
2022-10-18 21:24:58 +00:00
id: parsingErrorDialog
title: qsTranslate ( "expression" , "LogarithmPlotter - Parsing error" )
text: ""
function showDialog ( propName , propValue , error ) {
text = qsTranslate ( "expression" , "Error while parsing expression for property %1:\n%2\n\nEvaluated expression: %3" ) . arg ( propName ) . arg ( error ) . arg ( propValue )
open ( )
}
}
TextField {
id: editor
anchors.top: parent . top
anchors.left: labelItem . right
anchors.leftMargin: 5
width: control . width - ( labelItem . visible ? labelItem . width + 5 : 0 ) - iconLabel . width - 5
height: parent . height
verticalAlignment: TextInput . AlignVCenter
horizontalAlignment: control . label == "" ? TextInput.AlignLeft : TextInput . AlignHCenter
text: control . defValue
2023-05-22 05:22:33 +00:00
color: syntaxHighlightingEnabled ? sysPalette.window : sysPalette . windowText
2022-10-18 21:24:58 +00:00
focus: true
selectByMouse: true
2022-10-20 19:44:08 +00:00
property bool autocompleteEnabled: Helper . getSettingBool ( "autocompletion.enabled" )
property bool syntaxHighlightingEnabled: Helper . getSettingBool ( "expression_editor.colorize" )
property bool autoClosing: Helper . getSettingBool ( "expression_editor.autoclose" )
property var tokens: autocompleteEnabled || syntaxHighlightingEnabled ? parent . tokens ( text ) : [ ]
2022-10-19 21:44:04 +00:00
2022-10-18 21:24:58 +00:00
Keys.priority: Keys . BeforeItem // Required for knowing which key the user presses.
onEditingFinished: {
if ( insertButton . focus || insertPopup . focus ) return
let value = text
if ( value != "" && value . toString ( ) != defValue ) {
let expr = parse ( value )
if ( expr != null ) {
control . changed ( expr )
defValue = expr . toEditableString ( )
}
}
}
2022-10-19 21:44:04 +00:00
onActiveFocusChanged: {
2022-10-20 19:44:08 +00:00
if ( activeFocus && autocompleteEnabled )
2022-10-19 21:44:04 +00:00
autocompletePopup . open ( )
else
autocompletePopup . close ( )
}
2023-05-22 05:22:33 +00:00
cursorDelegate: Rectangle {
visible: editor . cursorVisible
color: sysPalette . windowText
width: editor . cursorRectangle . width
}
2022-10-19 21:44:04 +00:00
Keys.onUpPressed: function ( event ) {
2022-10-20 19:44:08 +00:00
if ( autocompleteEnabled )
if ( acPopupContent . itemSelected == 0 )
acPopupContent . itemSelected = acPopupContent . itemCount - 1
else
acPopupContent . itemSelected = acPopupContent . itemSelected - 1
2022-10-19 21:44:04 +00:00
event . accepted = true
}
Keys.onDownPressed: function ( event ) {
2022-10-20 19:44:08 +00:00
if ( autocompleteEnabled )
if ( acPopupContent . itemSelected == Math . min ( acPopupContent . itemCount - 1 ) )
acPopupContent . itemSelected = 0
else
acPopupContent . itemSelected = acPopupContent . itemSelected + 1
2022-10-19 21:44:04 +00:00
event . accepted = true
}
2022-10-18 21:24:58 +00:00
Keys.onPressed: function ( event ) {
2022-10-19 21:44:04 +00:00
// Autocomplete popup events
2022-10-20 19:44:08 +00:00
if ( autocompleteEnabled && ( event . key == Qt . Key_Enter || event . key == Qt . Key_Return ) && acPopupContent . itemCount > 0 ) {
2022-10-19 21:44:04 +00:00
acPopupContent . autocomplete ( )
event . accepted = true
} else
acPopupContent . itemSelected = 0
2022-10-20 19:44:08 +00:00
if ( event . text in openAndCloseMatches && autoClosing ) {
2022-10-18 21:24:58 +00:00
let start = selectionStart
insert ( selectionStart , event . text )
insert ( selectionEnd , openAndCloseMatches [ event . text ] )
cursorPosition = start + 1
event . accepted = true
}
}
2022-10-18 23:16:54 +00:00
Text {
id: colorizedEditor
anchors.fill: editor
verticalAlignment: TextInput . AlignVCenter
horizontalAlignment: control . label == "" ? TextInput.AlignLeft : TextInput . AlignHCenter
textFormat: Text . StyledText
2022-10-20 19:44:08 +00:00
text: parent . syntaxHighlightingEnabled ? colorize ( parent . tokens ) : ""
2022-10-18 23:16:54 +00:00
color: sysPalette . windowText
2022-10-20 19:44:08 +00:00
visible: parent . syntaxHighlightingEnabled
2022-10-18 23:53:53 +00:00
//font.pixelSize: parent.font.pixelSize
2022-10-18 23:16:54 +00:00
//opacity: editor.activeFocus ? 0 : 1
}
2022-10-19 21:44:04 +00:00
Popup {
id: autocompletePopup
x: 0
y: parent . height
closePolicy: Popup . NoAutoClose
width: editor . width
height: acPopupContent . height
padding: 0
Column {
id: acPopupContent
width: parent . width
readonly property var identifierTokenTypes: [
Parsing . TokenType . VARIABLE ,
Parsing . TokenType . FUNCTION ,
Parsing . TokenType . CONSTANT
]
2022-10-20 14:23:12 +00:00
property var currentToken: generateTokenInformation ( getTokenAt ( editor . tokens , editor . cursorPosition ) )
property var previousToken: generateTokenInformation ( getPreviousToken ( currentToken . token ) )
property var previousToken2: generateTokenInformation ( getPreviousToken ( previousToken . token ) )
property var previousToken3: generateTokenInformation ( getPreviousToken ( previousToken2 . token ) )
visible: currentToken . exists
2022-10-19 21:44:04 +00:00
// Focus handling.
2022-10-19 23:35:14 +00:00
readonly property var lists: [ objectPropertiesList , variablesList , constantsList , functionsList , executableObjectsList , objectsList ]
2022-10-20 14:23:12 +00:00
readonly property int itemCount: objectPropertiesList . model . length + variablesList . model . length + constantsList . model . length + functionsList . model . length + executableObjectsList . model . length + objectsList . model . length
2022-10-19 21:44:04 +00:00
property int itemSelected: 0
/ * !
2022-10-20 14:23:12 +00:00
\ qmlmethod var ExpressionEditor: : generateTokenInformation ( var token )
Generates basic information about the given token ( existence and type ) used in autocompletion ) .
* /
function generateTokenInformation ( token ) {
let exists = token != null
return {
'token' : token ,
'exists' : exists ,
'value' : exists ? token.value : null ,
'type' : exists ? token.type : null ,
'startPosition' : exists ? token.startPosition : 0 ,
'dot' : exists ? ( token . type == Parsing . TokenType . PUNCT && token . value == "." ) : false ,
'identifier' : exists ? identifierTokenTypes . includes ( token . type ) : false
}
}
/ * !
\ qmlmethod void ExpressionEditor: : autocompleteInfoAt ( int idx )
2022-10-19 21:44:04 +00:00
Returns the autocompletion text information at a given position .
The information contains key 'text' ( description text ) , 'autocomplete' ( text to insert )
and 'cursorFinalOffset' ( amount to add to the cursor ' s position after the end of the autocomplete )
* /
2022-10-20 14:23:12 +00:00
function autocompleteInfoAt ( idx ) {
2022-10-19 21:44:04 +00:00
if ( idx >= itemCount ) return ""
let startIndex = 0
for ( let list of lists ) {
if ( idx < startIndex + list . model . length )
return list . model [ idx - startIndex ]
startIndex += list . model . length
}
}
2022-10-20 14:23:12 +00:00
2022-10-19 21:44:04 +00:00
/ * !
2022-10-20 14:23:12 +00:00
\ qmlmethod void ExpressionEditor: : autocomplete ( )
2022-10-19 21:44:04 +00:00
Autocompletes with the current selected word .
* /
function autocomplete ( ) {
2022-10-20 14:23:12 +00:00
let autotext = autocompleteInfoAt ( itemSelected )
2022-10-19 21:44:04 +00:00
let startPos = currentToken . startPosition
2022-10-20 14:23:12 +00:00
console . log ( "Replacing" , currentToken . value , "at" , startPos , "with" , autotext . autocomplete )
2022-10-19 21:44:04 +00:00
editor . remove ( startPos , startPos + currentToken . value . length )
editor . insert ( startPos , autotext . autocomplete )
editor . cursorPosition = startPos + autotext . autocomplete . length + autotext . cursorFinalOffset
}
2022-10-20 14:23:12 +00:00
/ * !
\ qmlmethod var ExpressionEditor: : getPreviousToken ( var token )
Returns the token before this one .
* /
function getPreviousToken ( token ) {
let newToken = getTokenAt ( editor . tokens , token . startPosition )
if ( newToken != null && newToken . type == Parsing . TokenType . WHITESPACE )
return getPreviousToken ( newToken )
return newToken
}
2022-10-19 23:35:14 +00:00
AutocompletionCategory {
id: objectPropertiesList
category: qsTr ( "Object Properties" )
2022-10-21 13:40:01 +00:00
visbilityCondition: doesObjectExist
2022-10-19 23:35:14 +00:00
itemStartIndex: 0
itemSelected: parent . itemSelected
2022-10-20 14:23:12 +00:00
property bool isEnteringProperty: (
// Current token is dot.
( parent . currentToken . dot && parent . previousToken . identifier && ! parent . previousToken2 . dot ) ||
// Current token is property identifier
( parent . currentToken . identifier && parent . previousToken . dot && parent . previousToken2 . identifier && ! parent . previousToken3 . dot ) )
property string objectName: isEnteringProperty ?
( parent . currentToken . dot ? parent.previousToken.value : parent . previousToken2 . value )
: ""
2023-05-21 22:15:09 +00:00
property bool doesObjectExist: isEnteringProperty && ( objectName in Objects . currentObjectsByName )
2022-10-21 13:40:01 +00:00
property var objectProperties: doesObjectExist ?
2022-10-20 14:23:12 +00:00
Objects . currentObjectsByName [ objectName ] . constructor . properties ( ) :
{ }
categoryItems: Object . keys ( objectProperties )
autocompleteGenerator: ( item ) = > {
let propType = objectProperties [ item ]
return {
'text' : item , 'annotation' : propType == null ? '' : propType . toString ( ) ,
'autocomplete' : parent . currentToken . dot ? ` . $ { item } ` : ` $ { item } ` ,
'cursorFinalOffset' : 0
}
}
baseText: parent . visible && ! parent . currentToken . dot ? parent.currentToken.value : ""
2022-10-19 23:35:14 +00:00
}
2022-10-19 22:37:02 +00:00
AutocompletionCategory {
2022-10-19 23:14:09 +00:00
id: variablesList
2022-10-19 22:37:02 +00:00
2022-10-19 23:14:09 +00:00
category: qsTr ( "Variables" )
2022-10-20 14:23:12 +00:00
visbilityCondition: parent . currentToken . identifier && ! parent . previousToken . dot
2022-10-19 23:35:14 +00:00
itemStartIndex: objectPropertiesList . model . length
2022-10-19 23:14:09 +00:00
itemSelected: parent . itemSelected
categoryItems: control . variables
autocompleteGenerator: ( item ) = > { return {
'text' : item , 'annotation' : '' ,
'autocomplete' : item + " " , 'cursorFinalOffset' : 0
} }
baseText: parent . visible ? parent.currentToken.value : ""
}
AutocompletionCategory {
id: constantsList
2022-10-19 22:37:02 +00:00
category: qsTr ( "Constants" )
2022-10-20 14:23:12 +00:00
visbilityCondition: parent . currentToken . identifier && ! parent . previousToken . dot
2022-10-19 23:35:14 +00:00
itemStartIndex: variablesList . itemStartIndex + variablesList . model . length
2022-10-19 23:14:09 +00:00
itemSelected: parent . itemSelected
2022-10-19 22:37:02 +00:00
categoryItems: Parsing . CONSTANTS_LIST
2022-10-19 23:14:09 +00:00
autocompleteGenerator: ( item ) = > { return {
2023-10-09 21:28:29 +00:00
'text' : item , 'annotation' : Parsing . CONSTANTS [ item ] ,
2022-10-19 23:14:09 +00:00
'autocomplete' : item + " " , 'cursorFinalOffset' : 0
} }
baseText: parent . visible ? parent.currentToken.value : ""
2022-10-19 22:37:02 +00:00
}
AutocompletionCategory {
2022-10-19 21:44:04 +00:00
id: functionsList
2022-10-19 22:37:02 +00:00
category: qsTr ( "Functions" )
2022-10-20 14:23:12 +00:00
visbilityCondition: parent . currentToken . identifier && ! parent . previousToken . dot
2022-10-19 23:35:14 +00:00
itemStartIndex: constantsList . itemStartIndex + constantsList . model . length
2022-10-19 23:14:09 +00:00
itemSelected: parent . itemSelected
2022-10-19 22:37:02 +00:00
categoryItems: Parsing . FUNCTIONS_LIST
2022-10-19 23:14:09 +00:00
autocompleteGenerator: ( item ) = > { return {
2023-10-09 23:58:55 +00:00
'text' : item , 'annotation' : Parsing . FUNCTIONS_USAGE [ item ] . join ( ', ' ) ,
2022-10-19 23:14:09 +00:00
'autocomplete' : item + '()' , 'cursorFinalOffset' : - 1
} }
baseText: parent . visible ? parent.currentToken.value : ""
2022-10-19 22:37:02 +00:00
}
2022-10-19 21:44:04 +00:00
2022-10-19 22:37:02 +00:00
AutocompletionCategory {
2022-10-19 23:14:09 +00:00
id: executableObjectsList
2022-10-19 22:37:02 +00:00
category: qsTr ( "Executable Objects" )
2022-10-20 14:23:12 +00:00
visbilityCondition: parent . currentToken . identifier && ! parent . previousToken . dot
2022-10-19 23:35:14 +00:00
itemStartIndex: functionsList . itemStartIndex + functionsList . model . length
2022-10-19 23:14:09 +00:00
itemSelected: parent . itemSelected
categoryItems: Objects . getObjectsName ( "ExecutableObject" ) . filter ( obj = > obj != self )
autocompleteGenerator: ( item ) = > { return {
2022-10-20 14:23:12 +00:00
'text' : item , 'annotation' : Objects . currentObjectsByName [ item ] == null ? '' : Objects . currentObjectsByName [ item ] . constructor . displayType ( ) ,
2022-10-19 23:14:09 +00:00
'autocomplete' : item + '()' , 'cursorFinalOffset' : - 1
} }
baseText: parent . visible ? parent.currentToken.value : ""
}
AutocompletionCategory {
id: objectsList
category: qsTr ( "Objects" )
2022-10-20 14:23:12 +00:00
visbilityCondition: parent . currentToken . identifier && ! parent . previousToken . dot
2022-10-19 23:35:14 +00:00
itemStartIndex: executableObjectsList . itemStartIndex + executableObjectsList . model . length
2022-10-19 23:14:09 +00:00
itemSelected: parent . itemSelected
categoryItems: Object . keys ( Objects . currentObjectsByName ) . filter ( obj = > obj != self )
autocompleteGenerator: ( item ) = > { return {
'text' : item , 'annotation' : ` $ { Objects . currentObjectsByName [ item ] . constructor . displayType ( ) } ` ,
'autocomplete' : item + '.' , 'cursorFinalOffset' : 0
} }
baseText: parent . visible ? parent.currentToken.value : ""
2022-10-19 21:44:04 +00:00
}
}
}
2022-10-18 21:24:58 +00:00
}
Button {
id: insertButton
text: "α "
anchors.right: parent . right
anchors.rightMargin: 5
anchors.verticalCenter: parent . verticalCenter
width: 20
height: width
onClicked: {
insertPopup . open ( )
insertPopup . focus = true
}
}
2022-10-19 21:44:04 +00:00
P . InsertCharacter {
2022-10-18 21:24:58 +00:00
id: insertPopup
x: Math . round ( ( parent . width - width ) / 2 )
y: Math . round ( ( parent . height - height ) / 2 )
2023-10-09 15:55:06 +00:00
category: "expression"
2022-10-18 21:24:58 +00:00
onSelected: function ( c ) {
editor . insert ( editor . cursorPosition , c )
insertPopup . close ( )
2022-10-19 15:37:38 +00:00
focus = false
2022-10-18 21:24:58 +00:00
editor . focus = true
}
}
/ * !
\ qmlmethod var ExpressionEditor: : parse ( string newExpression )
Parses the \ c newExpression as an expression , checks for errors , shows them if any .
Returns the parsed expression if possible , null otherwise . .
* /
function parse ( newExpression ) {
let expr = null
try {
expr = new MathLib . Expression ( value . toString ( ) )
// Check if the expression is valid, throws error otherwise.
if ( ! expr . allRequirementsFullfilled ( ) ) {
let undefVars = expr . undefinedVariables ( )
if ( undefVars . length > 1 )
throw new Error ( qsTranslate ( 'error' , 'No object found with names %1.' ) . arg ( undefVars . join ( ', ' ) ) )
else
throw new Error ( qsTranslate ( 'error' , 'No object found with name %1.' ) . arg ( undefVars . join ( ', ' ) ) )
}
if ( expr . requiredObjects ( ) . includes ( control . self ) )
throw new Error ( qsTranslate ( 'error' , 'Object cannot be dependent on itself.' ) )
2022-10-20 16:10:49 +00:00
// Recursive dependencies
let dependentOnSelfObjects = expr . requiredObjects ( ) . filter (
( obj ) = > Objects . currentObjectsByName [ obj ] . getDependenciesList ( )
. includes ( Objects . currentObjectsByName [ control . self ] )
)
if ( dependentOnSelfObjects . length == 1 )
throw new Error ( qsTranslate ( 'error' , 'Circular dependency detected. Object %1 depends on %2.' ) . arg ( dependentOnSelfObjects [ 0 ] . toString ( ) ) . arg ( control . self ) )
else if ( dependentOnSelfObjects . length > 1 )
throw new Error ( qsTranslate ( 'error' , 'Circular dependency detected. Objects %1 depend on %2.' ) . arg ( dependentOnSelfObjects . map ( obj = > obj . toString ( ) ) . join ( ', ' ) ) . arg ( control . self ) )
2022-10-20 16:11:40 +00:00
//console.log(control.self, propertyName, expr.execute())
2022-10-20 15:39:36 +00:00
return expr
2022-10-18 21:24:58 +00:00
} catch ( e ) {
// Error in expression
parsingErrorDialog . showDialog ( propertyName , newExpression , e . message )
2022-10-20 15:39:36 +00:00
return null
2022-10-18 21:24:58 +00:00
}
}
2022-10-18 23:16:54 +00:00
/ * !
2022-10-19 21:44:04 +00:00
\ qmlmethod var ExpressionEditor: : tokens ( string expressionText )
Generates a list of tokens from the given .
2022-10-18 23:16:54 +00:00
* /
2022-10-19 21:44:04 +00:00
function tokens ( text ) {
2022-10-18 23:16:54 +00:00
let tokenizer = new Parsing . Tokenizer ( new Parsing . Input ( text ) , true , false )
2022-10-19 21:44:04 +00:00
let tokenList = [ ]
2022-10-18 23:16:54 +00:00
let token
2022-10-19 21:44:04 +00:00
while ( ( token = tokenizer . next ( ) ) != null )
tokenList . push ( token )
return tokenList
}
/ * !
\ qmlmethod var ExpressionEditor: : getTokenAt ( var tokens , int position )
Gets the token at the given position within the text .
Returns null if out of bounds .
* /
function getTokenAt ( tokenList , position ) {
let currentPosition = 0
for ( let token of tokenList )
if ( position <= ( currentPosition + token . value . length ) )
return token
else
currentPosition += token . value . length
return null
}
/ * !
\ qmlmethod var ExpressionEditor: : colorize ( var tokenList )
Creates an HTML colorized string of the given tokens .
Returns the colorized and escaped expression if possible , null otherwise . .
* /
function colorize ( tokenList ) {
let parsedText = ""
2023-05-22 03:17:12 +00:00
let scheme = colorSchemes [ Helper . getSettingInt ( "expression_editor.color_scheme" ) ]
2022-10-19 21:44:04 +00:00
for ( let token of tokenList ) {
2022-10-18 23:16:54 +00:00
switch ( token . type ) {
case Parsing.TokenType.VARIABLE:
2023-05-22 03:17:12 +00:00
parsedText += ` < font color = "${scheme.VARIABLE}" > $ { token . value } < / f o n t > `
2022-10-18 23:16:54 +00:00
break ;
case Parsing.TokenType.CONSTANT:
2023-05-22 03:17:12 +00:00
parsedText += ` < font color = "${scheme.CONSTANT}" > $ { token . value } < / f o n t > `
2022-10-18 23:16:54 +00:00
break ;
case Parsing.TokenType.FUNCTION:
2023-05-22 03:17:12 +00:00
parsedText += ` < font color = "${scheme.FUNCTION}" > $ { Utils . escapeHTML ( token . value ) } < / f o n t > `
2022-10-18 23:16:54 +00:00
break ;
case Parsing.TokenType.OPERATOR:
2023-05-22 03:17:12 +00:00
parsedText += ` < font color = "${scheme.OPERATOR}" > $ { Utils . escapeHTML ( token . value ) } < / f o n t > `
2022-10-18 23:16:54 +00:00
break ;
case Parsing.TokenType.NUMBER:
2023-05-22 03:17:12 +00:00
parsedText += ` < font color = "${scheme.NUMBER}" > $ { Utils . escapeHTML ( token . value ) } < / f o n t > `
2022-10-18 23:16:54 +00:00
break ;
case Parsing.TokenType.STRING:
2023-05-22 03:17:12 +00:00
parsedText += ` < font color = "${scheme.STRING}" > $ { token . limitator } $ { Utils . escapeHTML ( token . value ) } $ { token . limitator } < / f o n t > `
2022-10-18 23:16:54 +00:00
break ;
case Parsing.TokenType.WHITESPACE:
case Parsing.TokenType.PUNCT:
default:
parsedText += Utils . escapeHTML ( token . value ) . replace ( / /g , ' ' )
break ;
}
}
return parsedText
}
2022-10-18 21:24:58 +00:00
}