diff --git a/libnymea-app-core/scripting/codecompletion.cpp b/libnymea-app-core/scripting/codecompletion.cpp index 5176f568..718e2fab 100644 --- a/libnymea-app-core/scripting/codecompletion.cpp +++ b/libnymea-app-core/scripting/codecompletion.cpp @@ -15,6 +15,11 @@ CodeCompletion::CodeCompletion(QObject *parent): m_classes.insert("DeviceState", ClassInfo("DeviceState", {"id", "deviceId", "stateTypeId", "stateName", "value"}, {}, {"onValueChanged"})); m_classes.insert("DeviceEvent", ClassInfo("DeviceEvent", {"id", "deviceId", "eventTypeId", "eventName"}, {}, {"onTriggered"})); m_classes.insert("Timer", ClassInfo("Timer", {"id", "interval", "running", "repeat"}, {"start", "stop"}, {"onTriggered"})); + m_classes.insert("PropertyAnimation", ClassInfo("PropertyAnimation", {"id", "target", "targets", "property", "properties", "value", "from", "to", "easing", "exclude", "duration", "alwaysRunToEnd", "loops", "paused", "running"}, {"start", "stop", "pause", "resume", "complete"}, {"onStarted", "onStopped", "onFinished", "onRunningChanged"})); + m_classes.insert("ColorAnimation", ClassInfo("ColorAnimation", {"id", "target", "targets", "property", "properties", "value", "from", "to", "easing", "exclude", "duration", "alwaysRunToEnd", "loops", "paused", "running"}, {"start", "stop", "pause", "resume", "complete"}, {"onStarted", "onStopped", "onFinished", "onRunningChanged"})); + m_classes.insert("SequentialAnimation", ClassInfo("SequentialAnimation", {"id", "alwaysRunToEnd", "loops", "paused", "running"}, {"start", "stop", "pause", "resume", "complete"}, {"onStarted", "onStopped", "onFinished", "onRunningChanged"})); + m_classes.insert("ParallelAnimation", ClassInfo("ParallelAnimation", {"id", "alwaysRunToEnd", "loops", "paused", "running"}, {"start", "stop", "pause", "resume", "complete"}, {"onStarted", "onStopped", "onFinished", "onRunningChanged"})); + m_classes.insert("PauseAnimation", ClassInfo("PauseAnimation", {"id", "duration", "alwaysRunToEnd", "loops", "paused", "running"}, {"start", "stop", "pause", "resume", "complete"}, {"onStarted", "onStopped", "onFinished", "onRunningChanged"})); m_attachedClasses.insert("Component", ClassInfo("Component", {}, {}, {"onCompleted", "onDestruction", "onDestroyed"})); @@ -27,6 +32,7 @@ CodeCompletion::CodeCompletion(QObject *parent): m_genericJsSyntax.insert("do", "do "); m_genericJsSyntax.insert("if", "if "); m_genericJsSyntax.insert("else", "else "); + m_genericJsSyntax.insert("print", "print"); m_jsClasses.insert("console", ClassInfo("console", {}, {"log", "warn"})); m_jsClasses.insert("JSON", ClassInfo("JSON", {}, {"stringify", "parse", "hasOwnProperty", "isPrototypeOf", "toString", "valueOf", "toLocaleString", "propertyIsEnumerable"})); @@ -157,8 +163,10 @@ void CodeCompletion::update() } QRegExp stateNameExp(".*stateName: \"[a-zA-Z0-9-]*"); + qDebug() << "block text" << blockText << stateNameExp.exactMatch(blockText); if (stateNameExp.exactMatch(blockText)) { BlockInfo info = getBlockInfo(m_cursor.position()); + qDebug() << "stateName block info" << info.name << info.properties; if (!info.properties.contains("deviceId")) { return; } @@ -456,7 +464,6 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position) const info.end = m_document->textDocument()->find("}", position).position(); info.valid = true; - qDebug() << "Block strats at" << blockStart.position(); info.name = blockStart.block().text(); info.name.remove(QRegExp(" *\\{")); @@ -464,25 +471,27 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position) const info.name.remove(QRegExp(".* ")); } + qDebug() << "Block starts at" << blockStart.position() << "contents:" << blockStart.block().text(); int childBlocks = 0; while (!blockStart.isNull() && blockStart.position() < position) { - QTextCursor tmp = m_document->textDocument()->find(QRegExp("[{}\n]"), blockStart); - if (tmp.selectedText() == "{") { - blockStart = tmp; + QString line = blockStart.block().text(); + if (line.endsWith("{")) { + blockStart.movePosition(QTextCursor::NextBlock); childBlocks++; continue; } - if (tmp.selectedText() == "}") { - blockStart = tmp; + if (line.trimmed().startsWith("}")) { + blockStart.movePosition(QTextCursor::NextBlock); childBlocks--; continue; } // \n - if (childBlocks > 0) { // Skip all stuff in child blocks - blockStart = tmp; + if (childBlocks > 1) { // Skip all stuff in child blocks + blockStart.movePosition(QTextCursor::NextBlock); continue; } foreach (const QString &statement, blockStart.block().text().split(";")) { + qDebug() << "Have statement" << statement; QStringList parts = statement.split(":"); if (parts.length() != 2) { continue; @@ -492,7 +501,7 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position) const qDebug() << "inserting:" << propName << "->" << propValue; info.properties.insert(propName, propValue); } - blockStart = tmp; + blockStart.movePosition(QTextCursor::NextBlock); } return info; diff --git a/libnymea-app-core/scriptmanager.cpp b/libnymea-app-core/scriptmanager.cpp index c3a21990..1d0ea1b7 100644 --- a/libnymea-app-core/scriptmanager.cpp +++ b/libnymea-app-core/scriptmanager.cpp @@ -124,11 +124,16 @@ void ScriptManager::onNotificationReceived(const QVariantMap ¶ms) Script *script = new Script(scriptMap.value("id").toUuid()); script->setName(scriptMap.value("name").toString()); m_scripts->addScript(script); + emit addScriptReply(params.value("id").toInt(), + params.value("params").toMap().value("scriptError").toString(), + params.value("params").toMap().value("scriptId").toUuid(), + params.value("params").toMap().value("errors").toStringList()); } else if (params.value("notification").toString() == "Scripts.ScriptRemoved") { - QUuid id = params.value("params").toMap().value("scriptId").toUuid(); + QUuid id = params.value("params").toMap().value("id").toUuid(); m_scripts->removeScript(id); + emit removeScriptReply(params.value("id").toInt(), params.value("params").toMap().value("scriptError").toString()); } else if (params.value("notification").toString() == "Scripts.ScriptChanged") { diff --git a/nymea-app/ui/MagicPage.qml b/nymea-app/ui/MagicPage.qml index 0ac54c2b..88369d11 100644 --- a/nymea-app/ui/MagicPage.qml +++ b/nymea-app/ui/MagicPage.qml @@ -28,7 +28,7 @@ Page { RuleTemplatesFilterModel { id: ruleTemplatesModel ruleTemplates: RuleTemplates {} - readonly property var deviceClass: device ? engine.deviceManager.deviceClasses.getDeviceClass(root.device.deviceClassId) : null + readonly property var deviceClass: root.device ? engine.deviceManager.deviceClasses.getDeviceClass(root.device.deviceClassId) : null filterByDevices: DevicesProxy { engine: _engine } filterInterfaceNames: deviceClass ? deviceClass.interfaces : [] } diff --git a/nymea-app/ui/magic/ScriptEditor.qml b/nymea-app/ui/magic/ScriptEditor.qml index 8a8e62d6..76ba4078 100644 --- a/nymea-app/ui/magic/ScriptEditor.qml +++ b/nymea-app/ui/magic/ScriptEditor.qml @@ -47,7 +47,7 @@ Page { HeaderButton { imageSource: "../images/save.svg" - enabled: d.script.name !== nameTextField.text || d.oldContent !== scriptEdit.text + enabled: d.script && d.script.name !== nameTextField.text || d.oldContent !== scriptEdit.text color: enabled ? app.accentColor : keyColor hoverEnabled: true ToolTip.text: qsTr("Deploy script") @@ -90,6 +90,7 @@ Page { d.callId = -1; if (scriptError == "ScriptErrorNoError") { d.scriptId = scriptId; + d.oldContent = scriptEdit.text; } errorModel.update(errors); } @@ -97,8 +98,10 @@ Page { onEditScriptReply: { print("edit reply", id, d.callId) if (id == d.callId) { - d.oldContent = scriptEdit.text; d.callId = -1; + if (scriptError == "ScriptErrorNoError") { + d.oldContent = scriptEdit.text; + } errorModel.update(errors) } } @@ -141,6 +144,7 @@ Page { LineNumbers { id: lineNumbers + textArea: scriptEdit } TextArea.flickable: TextArea { @@ -217,6 +221,20 @@ Page { completionBox.show(); event.accepted = true; return; + case Qt.Key_Plus: + if (event.modifiers & Qt.ControlModifier) { + scriptEdit.font.pixelSize++; + event.accepted = true; + return; + } + break; + case Qt.Key_Minus: + if (event.modifiers & Qt.ControlModifier) { + scriptEdit.font.pixelSize--; + event.accepted = true; + return; + } + } // Things to do only when we're autocompleting @@ -289,8 +307,7 @@ Page { delegate: Label { width: parent.width text: model.line + ":" + model.column + ": " + model.message - font.pixelSize: app.extraSmallFont - font.family: "Monospace" + font: scriptEdit.font } } } @@ -324,8 +341,7 @@ Page { delegate: Label { width: parent.width text: model.message - font.pixelSize: app.extraSmallFont - font.family: "Monospace" + font: scriptEdit.font color: model.type === "ScriptMessageTypeWarning" ? "red" : app.foregroundColor } } diff --git a/nymea-app/ui/magic/ScriptsPage.qml b/nymea-app/ui/magic/ScriptsPage.qml index 4ae185ec..37c38853 100644 --- a/nymea-app/ui/magic/ScriptsPage.qml +++ b/nymea-app/ui/magic/ScriptsPage.qml @@ -24,7 +24,7 @@ Page { Connections { target: engine.scriptManager - onRemovScriptReply: { + onRemoveScriptReply: { if (id == d.pendingAction) { d.pendingAction = -1; } @@ -48,6 +48,17 @@ Page { d.pendingAction = engine.scriptManager.removeScript(model.id); } } + + EmptyViewPlaceholder { + anchors.centerIn: parent + title: qsTr("No scripts are installed yet.") + text: qsTr("Press \"Add script\" to get started.") + imageSource: "../images/script.svg" + buttonText: qsTr("Add script") + onButtonClicked: { + pageStack.push("ScriptEditor.qml"); + } + } } BusyOverlay { diff --git a/nymea-app/ui/magic/scripting/LineNumbers.qml b/nymea-app/ui/magic/scripting/LineNumbers.qml index 375f491e..16a89e73 100644 --- a/nymea-app/ui/magic/scripting/LineNumbers.qml +++ b/nymea-app/ui/magic/scripting/LineNumbers.qml @@ -1,13 +1,21 @@ -import QtQuick 2.2 +import QtQuick 2.4 import QtQuick.Controls 2.2 Rectangle { - id: lineNumbers + id: root + + property TextArea textArea: null + + FontMetrics { + id: fontMetrics + font: textArea.font + } + width: { - var ret = 10; + var ret = fontMetrics.maximumCharacterWidth * 2; var tmp = scriptEdit.lineCount while (tmp >= 10) { - ret += 10; + ret += fontMetrics.maximumCharacterWidth; tmp /= 10; } return ret; @@ -25,11 +33,11 @@ Rectangle { anchors.fill: parent anchors.topMargin: 8 Repeater { - model: scriptEdit.lineCount + model: root.textArea.lineCount delegate: Rectangle { id: lineNumberDelegate width: parent.width - height: scriptEdit.contentHeight / scriptEdit.lineCount + height: root.textArea.contentHeight / root.textArea.lineCount color: hasError ? "#FF0000" : "transparent" readonly property bool hasError: errorModel.errorLines.indexOf(index + 1) >= 0 Label { @@ -38,8 +46,8 @@ Rectangle { anchors.right: parent.right anchors.rightMargin: 3 text: index + 1 - font.pixelSize: scriptEdit.font.pixelSize - font.family: scriptEdit.font.family + font.pixelSize: root.textArea.font.pixelSize + font.family: root.textArea.font.family font.weight: Font.Light color: lineNumberDelegate.hasError ? "#FFFFFF" : "#808080" }