This repository has been archived on 2026-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
powersync-app/nymea-app/ui/magic/ScriptEditor.qml
2020-01-16 11:43:20 +01:00

306 lines
11 KiB
QML

import QtQuick 2.0
import QtQuick.Controls 2.2
import Nymea 1.0
import QtQuick.Layouts 1.2
import QtQuick.Controls.Material 2.1
import "../components"
import "scripting"
Page {
id: root
property alias scriptId: d.scriptId
Component.onCompleted: {
if (scriptId !== undefined) {
d.callId = engine.scriptManager.fetchScript(scriptId);
} else {
scriptEdit.text = "import QtQuick 2.0\nimport nymea 1.0\n\nItem {\n \n}\n"
d.callId = engine.scriptManager.addScript(scriptEdit.text);
}
}
header: NymeaHeader {
text: qsTr("Script editor")
onBackPressed: {
if (scriptEdit.text == d.oldContent) {
pageStack.pop()
return;
}
var comp = Qt.createComponent("../components/MeaDialog.qml");
var popup = comp.createObject(root, {
title: qsTr("Unsaved changes"),
text: qsTr("There are unsaved changes in the script. Do you want to discard the changes?"),
standardButtons: Dialog.Yes | Dialog.No
})
popup.onAccepted.connect(function() {
pageStack.pop();
});
popup.open();
}
HeaderButton {
imageSource: "../images/media-playback-start.svg"
onClicked: {
if (!d.scriptId) {
d.callId = engine.scriptManager.addScript(scriptEdit.text)
} else {
print("editing script", d.scriptId, scriptEdit.text)
d.callId = engine.scriptManager.editScript(d.scriptId, scriptEdit.text)
}
}
}
}
QtObject {
id: d
property int callId
property var scriptId
property string oldContent
}
Connections {
target: engine.scriptManager
onScriptAdded: {
if (id == d.callId) {
if (scriptError == "ScriptErrorNoError") {
d.scriptId = scriptId;
}
errorModel.update(errors);
}
}
onScriptEdited: {
if (id == d.callId) {
errorModel.update(errors)
}
}
onScriptFetched: {
if (id == d.callId && scriptError == "ScriptErrorNoError") {
scriptEdit.text = content;
d.oldContent = content;
}
}
onScriptMessage: {
if (scriptId !== d.scriptId) {
return;
}
messagesModel.append({type: type, message: message})
}
}
// TODO: Make this a SplitView when we can use Qt 5.13
ColumnLayout {
anchors.fill: parent
Flickable {
id: scriptFlickable
Layout.fillHeight: true
Layout.fillWidth: true
clip: true
boundsBehavior: Flickable.StopAtBounds
ScrollBar.vertical: ScrollBar { policy: ScrollBar.AlwaysOn }
ScrollBar.horizontal: ScrollBar { policy: ScrollBar.AlwaysOn }
LineNumbers {
id: lineNumbers
}
TextArea.flickable: TextArea {
id: scriptEdit
leftPadding: lineNumbers.width + 2
rightPadding: 20
bottomPadding: 28
font.family: "Monospace"
font.pixelSize: app.extraSmallFont
selectByMouse: true
selectByKeyboard: true
onCursorPositionChanged: {
if (completionBox.visible) {
completion.update();
}
}
Keys.onPressed: {
print("key", event.key, "Completion box visible:", completionBox.visible)
// Things to happen only when we're not autocompleting
if (!completionBox.visible) {
switch (event.key) {
case Qt.Key_Return:
case Qt.Key_Enter:
completion.newLine();
event.accepted = true;
return;
case Qt.Key_Space:
if (!completionBox.visible && (event.modifiers & Qt.ControlModifier)) {
completion.update();
completionBox.show();
return;
}
}
}
// things to happen in any case
switch (event.key) {
case Qt.Key_BraceLeft:
completion.insertAfterCursor("}");
return;
case Qt.Key_BraceRight:
completion.closeBlock();
event.accepted = true;
return;
case Qt.Key_Tab:
completion.indent(selectionStart, selectionEnd);
event.accepted = true;
return;
case Qt.Key_Backtab:
completion.unindent(selectionStart, selectionEnd);
event.accepted = true;
return;
}
// Things to do only when we're autocompleting
if (completionBox.visible) {
switch (event.key) {
case Qt.Key_Escape:
completionBox.hide();
event.accepted = true;
break;
case Qt.Key_Down:
completionBox.next();
event.accepted = true;
break;
case Qt.Key_Up:
completionBox.previous();
event.accepted = true;
break;
case Qt.Key_Enter:
case Qt.Key_Return:
completion.complete(completionBox.currentIndex)
completionBox.hide();
event.accepted = true;
break;
}
}
}
CompletionBox {
id: completionBox
model: completion.model
textArea: scriptEdit
onComplete: {
completion.complete(index)
}
}
}
}
EditorPane {
Layout.fillWidth: true
Layout.preferredHeight: Math.min(implicitHeight, root.height / 4)
ScrollView {
id: errorsPane
anchors { fill: parent; margins: app.margins / 2 }
property string title: qsTr("Errors")
signal raise()
ListView {
id: errorListView
model: ListModel {
id: errorModel
property var errorLines: []
function update(errors) {
clear();
var newErrorLines = []
errors.forEach( function(error) {
var parts = error.split(":")
append({line: parseInt(parts[0]), column: parseInt(parts[1]), message: parts[2].trim()})
newErrorLines.push(parseInt(parts[0]));
})
errorLines = newErrorLines;
if (errorModel.count > 0) {
errorsPane.raise();
}
}
function getError(lineNumber) {
print("getting error for line", lineNumber, errorModel.count)
for (var i = 0; i < errorModel.count; i++) {
var entry = get(i);
print("i:", i, entry.message, entry.line)
if (entry.line === lineNumber) {
return entry;
}
}
}
}
delegate: Label {
width: parent.width
text: model.line + ":" + model.column + ": " + model.message
font.pixelSize: app.extraSmallFont
font.family: "Monospace"
}
}
}
ScrollView {
id: consolePane
anchors {fill: parent; margins: app.margins/ 2 }
property string title: qsTr("Console")
signal raise()
ListView {
id: messagesListView
model: ListModel {
id: messagesModel
onCountChanged: {
if (count > 0) {
consolePane.raise();
}
}
}
property bool autoScroll: true
onCountChanged: {
if (autoScroll) {
messagesListView.positionViewAtEnd()
}
}
onMovementEnded: {
autoScroll = messagesListView.atYEnd;
}
delegate: Label {
width: parent.width
text: model.message
font.pixelSize: app.extraSmallFont
font.family: "Monospace"
color: model.type === "ScriptMessageTypeWarning" ? "red" : app.foregroundColor
}
}
}
}
}
ScriptSyntaxHighlighter {
id: syntax
document: scriptEdit.textDocument
backgroundColor: app.backgroundColor
}
CodeCompletion {
id: completion
engine: _engine
document: scriptEdit.textDocument
cursorPosition: scriptEdit.cursorPosition
onCursorPositionChanged: scriptEdit.cursorPosition = cursorPosition
}
}