More work on the editor
This commit is contained in:
parent
687912a82c
commit
59047704ae
@ -1,6 +1,5 @@
|
|||||||
#include "codecompletion.h"
|
#include "codecompletion.h"
|
||||||
|
|
||||||
#include "completionmodel.h"
|
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@ -11,13 +10,26 @@
|
|||||||
CodeCompletion::CodeCompletion(QObject *parent):
|
CodeCompletion::CodeCompletion(QObject *parent):
|
||||||
QObject(parent)
|
QObject(parent)
|
||||||
{
|
{
|
||||||
registerType<QQuickItem>("Item");
|
m_classes.insert("Item", ClassInfo("Item", {"id"}));
|
||||||
m_classes.insert("DeviceAction", {"id", "deviceId", "actionTypeId", "actionName"});
|
m_classes.insert("DeviceAction", ClassInfo("DeviceAction", {"id", "deviceId", "actionTypeId", "actionName"}, {"execute"}));
|
||||||
m_classes.insert("DeviceState", {"id", "deviceId", "stateTypeId", "stateName", "value", "onValueChanged"});
|
m_classes.insert("DeviceState", ClassInfo("DeviceState", {"id", "deviceId", "stateTypeId", "stateName", "value"}, {}, {"onValueChanged"}));
|
||||||
m_classes.insert("DeviceEvent", {"id", "deviceId", "eventTypeId", "eventName", "onTriggered"});
|
m_classes.insert("DeviceEvent", ClassInfo("DeviceEvent", {"id", "deviceId", "eventTypeId", "eventName"}, {}, {"onTriggered"}));
|
||||||
m_classes.insert("Timer", {"id", "interval", "running", "repeat", "onTriggered"});
|
m_classes.insert("Timer", ClassInfo("Timer", {"id", "interval", "running", "repeat"}, {"start", "stop"}, {"onTriggered"}));
|
||||||
|
|
||||||
|
m_attachedClasses.insert("Component", ClassInfo("Component", {}, {}, {"onCompleted", "onDestruction", "onDestroyed"}));
|
||||||
|
|
||||||
m_genericSyntax.insert("property", "property ");
|
m_genericSyntax.insert("property", "property ");
|
||||||
|
m_genericSyntax.insert("function", "function ");
|
||||||
|
|
||||||
|
m_genericJsSyntax.insert("for", "for");
|
||||||
|
m_genericJsSyntax.insert("var", "var");
|
||||||
|
m_genericJsSyntax.insert("while", "while ");
|
||||||
|
m_genericJsSyntax.insert("do", "do ");
|
||||||
|
m_genericJsSyntax.insert("if", "if ");
|
||||||
|
m_genericJsSyntax.insert("else", "else ");
|
||||||
|
|
||||||
|
m_jsClasses.insert("console", ClassInfo("console", {}, {"log", "warn"}));
|
||||||
|
m_jsClasses.insert("JSON", ClassInfo("JSON", {}, {"stringify", "parse", "hasOwnProperty", "isPrototypeOf", "toString", "valueOf", "toLocaleString", "propertyIsEnumerable"}));
|
||||||
|
|
||||||
m_model = new CompletionModel(this);
|
m_model = new CompletionModel(this);
|
||||||
m_proxy = new CompletionProxyModel(m_model, this);
|
m_proxy = new CompletionProxyModel(m_model, this);
|
||||||
@ -51,7 +63,6 @@ void CodeCompletion::setDocument(QQuickTextDocument *document)
|
|||||||
emit cursorPositionChanged();
|
emit cursorPositionChanged();
|
||||||
|
|
||||||
connect(m_document->textDocument(), &QTextDocument::cursorPositionChanged, this, [this](const QTextCursor &cursor){
|
connect(m_document->textDocument(), &QTextDocument::cursorPositionChanged, this, [this](const QTextCursor &cursor){
|
||||||
qDebug() << "text cursor changed" << cursor.position();
|
|
||||||
m_cursor = cursor;
|
m_cursor = cursor;
|
||||||
update();
|
update();
|
||||||
});
|
});
|
||||||
@ -65,7 +76,6 @@ int CodeCompletion::cursorPosition() const
|
|||||||
|
|
||||||
void CodeCompletion::setCursorPosition(int position)
|
void CodeCompletion::setCursorPosition(int position)
|
||||||
{
|
{
|
||||||
qDebug() << "setCursorPos" << position << m_cursor.position();
|
|
||||||
// This is a bit tricky: As our cursor works on the same textDocument as the view,
|
// This is a bit tricky: As our cursor works on the same textDocument as the view,
|
||||||
// our cursor will already have the position set to the new one by the time we
|
// our cursor will already have the position set to the new one by the time we
|
||||||
// receive the update from the View when the document is changed.
|
// receive the update from the View when the document is changed.
|
||||||
@ -104,7 +114,9 @@ void CodeCompletion::update()
|
|||||||
}
|
}
|
||||||
lastUpdatePos = m_cursor.position();
|
lastUpdatePos = m_cursor.position();
|
||||||
|
|
||||||
QString blockText = m_cursor.block().text();
|
QTextCursor tmp = m_cursor;
|
||||||
|
tmp.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
|
||||||
|
QString blockText = tmp.selectedText();
|
||||||
|
|
||||||
QList<CompletionModel::Entry> entries;
|
QList<CompletionModel::Entry> entries;
|
||||||
|
|
||||||
@ -112,8 +124,7 @@ void CodeCompletion::update()
|
|||||||
if (deviceIdExp.exactMatch(blockText)) {
|
if (deviceIdExp.exactMatch(blockText)) {
|
||||||
for (int i = 0; i < m_engine->deviceManager()->devices()->rowCount(); i++) {
|
for (int i = 0; i < m_engine->deviceManager()->devices()->rowCount(); i++) {
|
||||||
Device *dev = m_engine->deviceManager()->devices()->get(i);
|
Device *dev = m_engine->deviceManager()->devices()->get(i);
|
||||||
entries.append(CompletionModel::Entry(dev->id().toString(), dev->name(), true, true));
|
entries.append(CompletionModel::Entry(dev->id().toString() + "\" // " + dev->name(), dev->name(), "thing", dev->deviceClass()->interfaces().join(",")));
|
||||||
|
|
||||||
}
|
}
|
||||||
blockText.remove(QRegExp(".*deviceId: \""));
|
blockText.remove(QRegExp(".*deviceId: \""));
|
||||||
m_model->update(entries);
|
m_model->update(entries);
|
||||||
@ -137,7 +148,7 @@ void CodeCompletion::update()
|
|||||||
|
|
||||||
for (int i = 0; i < device->deviceClass()->stateTypes()->rowCount(); i++) {
|
for (int i = 0; i < device->deviceClass()->stateTypes()->rowCount(); i++) {
|
||||||
StateType *stateType = device->deviceClass()->stateTypes()->get(i);
|
StateType *stateType = device->deviceClass()->stateTypes()->get(i);
|
||||||
entries.append(CompletionModel::Entry(stateType->id(), stateType->name(), true, true));
|
entries.append(CompletionModel::Entry(stateType->id() + "\" // " + stateType->name(), stateType->name(), "stateType"));
|
||||||
}
|
}
|
||||||
blockText.remove(QRegExp(".*stateTypeId: \""));
|
blockText.remove(QRegExp(".*stateTypeId: \""));
|
||||||
m_model->update(entries);
|
m_model->update(entries);
|
||||||
@ -161,7 +172,7 @@ void CodeCompletion::update()
|
|||||||
|
|
||||||
for (int i = 0; i < device->deviceClass()->stateTypes()->rowCount(); i++) {
|
for (int i = 0; i < device->deviceClass()->stateTypes()->rowCount(); i++) {
|
||||||
StateType *stateType = device->deviceClass()->stateTypes()->get(i);
|
StateType *stateType = device->deviceClass()->stateTypes()->get(i);
|
||||||
entries.append(CompletionModel::Entry(stateType->name(), stateType->name(), true, false));
|
entries.append(CompletionModel::Entry(stateType->name() + "\"", stateType->name(), "stateType"));
|
||||||
}
|
}
|
||||||
blockText.remove(QRegExp(".*stateName: \""));
|
blockText.remove(QRegExp(".*stateName: \""));
|
||||||
m_model->update(entries);
|
m_model->update(entries);
|
||||||
@ -185,7 +196,7 @@ void CodeCompletion::update()
|
|||||||
|
|
||||||
for (int i = 0; i < device->deviceClass()->actionTypes()->rowCount(); i++) {
|
for (int i = 0; i < device->deviceClass()->actionTypes()->rowCount(); i++) {
|
||||||
ActionType *actionType = device->deviceClass()->actionTypes()->get(i);
|
ActionType *actionType = device->deviceClass()->actionTypes()->get(i);
|
||||||
entries.append(CompletionModel::Entry(actionType->id(), actionType->name(), true, true));
|
entries.append(CompletionModel::Entry(actionType->id() + "\" // " + actionType->name(), actionType->name(), "actionType"));
|
||||||
}
|
}
|
||||||
blockText.remove(QRegExp(".*actionTypeId: \""));
|
blockText.remove(QRegExp(".*actionTypeId: \""));
|
||||||
m_model->update(entries);
|
m_model->update(entries);
|
||||||
@ -209,7 +220,7 @@ void CodeCompletion::update()
|
|||||||
|
|
||||||
for (int i = 0; i < device->deviceClass()->actionTypes()->rowCount(); i++) {
|
for (int i = 0; i < device->deviceClass()->actionTypes()->rowCount(); i++) {
|
||||||
ActionType *actionType = device->deviceClass()->actionTypes()->get(i);
|
ActionType *actionType = device->deviceClass()->actionTypes()->get(i);
|
||||||
entries.append(CompletionModel::Entry(actionType->name(), actionType->name(), true, false));
|
entries.append(CompletionModel::Entry(actionType->name() + "\"", actionType->name(), "actionType"));
|
||||||
}
|
}
|
||||||
blockText.remove(QRegExp(".*actionName: \""));
|
blockText.remove(QRegExp(".*actionName: \""));
|
||||||
m_model->update(entries);
|
m_model->update(entries);
|
||||||
@ -232,7 +243,7 @@ void CodeCompletion::update()
|
|||||||
|
|
||||||
for (int i = 0; i < device->deviceClass()->eventTypes()->rowCount(); i++) {
|
for (int i = 0; i < device->deviceClass()->eventTypes()->rowCount(); i++) {
|
||||||
EventType *eventType = device->deviceClass()->eventTypes()->get(i);
|
EventType *eventType = device->deviceClass()->eventTypes()->get(i);
|
||||||
entries.append(CompletionModel::Entry(eventType->id(), eventType->name(), true, true));
|
entries.append(CompletionModel::Entry(eventType->id() + "\" // " + eventType->name(), eventType->name(), "eventType"));
|
||||||
}
|
}
|
||||||
blockText.remove(QRegExp(".*eventTypeId: \""));
|
blockText.remove(QRegExp(".*eventTypeId: \""));
|
||||||
m_model->update(entries);
|
m_model->update(entries);
|
||||||
@ -255,7 +266,7 @@ void CodeCompletion::update()
|
|||||||
|
|
||||||
for (int i = 0; i < device->deviceClass()->eventTypes()->rowCount(); i++) {
|
for (int i = 0; i < device->deviceClass()->eventTypes()->rowCount(); i++) {
|
||||||
EventType *eventType = device->deviceClass()->eventTypes()->get(i);
|
EventType *eventType = device->deviceClass()->eventTypes()->get(i);
|
||||||
entries.append(CompletionModel::Entry(eventType->name(), eventType->name(), true, false));
|
entries.append(CompletionModel::Entry(eventType->name() + "\"", eventType->name(), "eventType"));
|
||||||
}
|
}
|
||||||
blockText.remove(QRegExp(".*eventName: \""));
|
blockText.remove(QRegExp(".*eventName: \""));
|
||||||
m_model->update(entries);
|
m_model->update(entries);
|
||||||
@ -265,7 +276,7 @@ void CodeCompletion::update()
|
|||||||
|
|
||||||
QRegExp importExp("imp(o|or)?");
|
QRegExp importExp("imp(o|or)?");
|
||||||
if (importExp.exactMatch(blockText)) {
|
if (importExp.exactMatch(blockText)) {
|
||||||
entries.append(CompletionModel::Entry("import ", "import"));
|
entries.append(CompletionModel::Entry("import ", "import", "keyword", ""));
|
||||||
m_model->update(entries);
|
m_model->update(entries);
|
||||||
m_proxy->setFilter(blockText);
|
m_proxy->setFilter(blockText);
|
||||||
return;
|
return;
|
||||||
@ -281,7 +292,7 @@ void CodeCompletion::update()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QRegExp rValueExp(" *[a-zA-Z0-0]+:[ a-zA-Z0-0]*");
|
QRegExp rValueExp(" *[\\.a-zA-Z0-0]+:[ a-zA-Z0-0]*");
|
||||||
if (rValueExp.exactMatch(blockText)) {
|
if (rValueExp.exactMatch(blockText)) {
|
||||||
QTextCursor tmp = m_cursor;
|
QTextCursor tmp = m_cursor;
|
||||||
tmp.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
|
tmp.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
|
||||||
@ -297,55 +308,121 @@ void CodeCompletion::update()
|
|||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "rValue" << previousWord << word;
|
qDebug() << "rValue" << previousWord << word;
|
||||||
// Find all ids in the doc
|
entries.append(getIds());
|
||||||
tmp = QTextCursor(m_document->textDocument());
|
foreach (const QString &s, m_jsClasses.keys()) {
|
||||||
while (!tmp.atEnd()) {
|
entries.append(CompletionModel::Entry(s, s, "type"));
|
||||||
tmp.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
|
|
||||||
tmp.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
|
||||||
QString word = tmp.selectedText();
|
|
||||||
if (word == "id") {
|
|
||||||
tmp.movePosition(QTextCursor::NextWord, QTextCursor::MoveAnchor);
|
|
||||||
tmp.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
|
||||||
QString idName = tmp.selectedText();
|
|
||||||
entries.append(CompletionModel::Entry(idName, idName));
|
|
||||||
}
|
|
||||||
tmp.movePosition(QTextCursor::NextWord);
|
|
||||||
}
|
}
|
||||||
|
foreach (const QString &s, m_attachedClasses.keys()) {
|
||||||
|
entries.append(CompletionModel::Entry(s, s, "type"));
|
||||||
|
}
|
||||||
|
|
||||||
m_model->update(entries);
|
m_model->update(entries);
|
||||||
m_proxy->setFilter(word);
|
m_proxy->setFilter(word);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRegExp dotExp(".*[a-zA-Z0-9]+\\.[a-zA-Z0-9]*");
|
||||||
|
if (dotExp.exactMatch(blockText)) {
|
||||||
|
QString id = blockText;
|
||||||
|
id.remove(QRegExp(".* ")).remove(QRegExp("\\.[a-zA-Z0-9]*"));
|
||||||
|
QString type = getIdTypes().value(id);
|
||||||
|
qDebug() << "dot expression:" << id << type;
|
||||||
|
// Classes
|
||||||
|
foreach (const QString &property, m_classes.value(type).properties) {
|
||||||
|
entries.append(CompletionModel::Entry(property, property, "property"));
|
||||||
|
}
|
||||||
|
foreach (const QString &method, m_classes.value(type).methods) {
|
||||||
|
entries.append(CompletionModel::Entry(method + "(", method, "method", "", ")"));
|
||||||
|
}
|
||||||
|
// Attached classes/properties
|
||||||
|
foreach (const QString &property, m_attachedClasses.value(id).properties) {
|
||||||
|
entries.append(CompletionModel::Entry(property, property, "property"));
|
||||||
|
}
|
||||||
|
foreach (const QString &method, m_attachedClasses.value(id).methods) {
|
||||||
|
entries.append(CompletionModel::Entry(method + "(", method, "method", "", ")"));
|
||||||
|
}
|
||||||
|
foreach (const QString &event, m_attachedClasses.value(id).events) {
|
||||||
|
entries.append(CompletionModel::Entry(event + ": ", event, "event"));
|
||||||
|
}
|
||||||
|
// JS global objects
|
||||||
|
foreach (const QString &property, m_jsClasses.value(id).properties) {
|
||||||
|
entries.append(CompletionModel::Entry(property, property, "property"));
|
||||||
|
}
|
||||||
|
foreach (const QString &method, m_jsClasses.value(id).methods) {
|
||||||
|
entries.append(CompletionModel::Entry(method + "(", method, "method", "", ")"));
|
||||||
|
}
|
||||||
|
m_model->update(entries);
|
||||||
|
m_proxy->setFilter(blockText.remove(QRegExp(".*\\.")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Are we in a JS block?
|
||||||
|
int pos = m_cursor.position();
|
||||||
|
BlockInfo jsBlock = getBlockInfo(pos);
|
||||||
|
bool isImperative = jsBlock.name.endsWith(":") || jsBlock.name.endsWith("()");
|
||||||
|
bool atStart = false;
|
||||||
|
while (!isImperative && jsBlock.valid && !atStart) {
|
||||||
|
qDebug() << "is imperative block?" << isImperative << jsBlock.name << "blockText" << blockText;
|
||||||
|
BlockInfo tmp = getBlockInfo(jsBlock.start - 1);
|
||||||
|
if (tmp.valid) {
|
||||||
|
jsBlock = tmp;
|
||||||
|
isImperative = jsBlock.name.endsWith(":") || jsBlock.name.endsWith("()");
|
||||||
|
} else {
|
||||||
|
atStart = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isImperative) {
|
||||||
|
// Starting a new expression?
|
||||||
|
QRegExp newExpressionExp("(.*; [a-zA-Z0-9]*| *[a-zA-Z0-9]*)");
|
||||||
|
if (newExpressionExp.exactMatch(blockText)) {
|
||||||
|
// Add generic qml syntax
|
||||||
|
foreach (const QString &s, m_genericJsSyntax.keys()) {
|
||||||
|
entries.append(CompletionModel::Entry(m_genericJsSyntax.value(s), s, "keyword", ""));
|
||||||
|
}
|
||||||
|
// Add js global objects
|
||||||
|
foreach (const QString &s, m_jsClasses.keys()) {
|
||||||
|
entries.append(CompletionModel::Entry(s, s, "type"));
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.append(getIds());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_model->update(entries);
|
||||||
|
m_proxy->setFilter(blockText.remove(QRegExp(".* ")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QRegExp lValueStartExp(" *[a-zA-Z0-9]*");
|
QRegExp lValueStartExp(" *[a-zA-Z0-9]*");
|
||||||
if (lValueStartExp.exactMatch(blockText)) {
|
if (lValueStartExp.exactMatch(blockText)) {
|
||||||
qDebug() << "matching";
|
BlockInfo blockInfo = getBlockInfo(m_cursor.position());
|
||||||
QTextCursor blockStartCursor = m_document->textDocument()->find("{", m_cursor, QTextDocument::FindBackward);
|
|
||||||
QTextCursor blockEndCursor = m_document->textDocument()->find("}", m_cursor, QTextDocument::FindBackward);
|
|
||||||
while (!blockEndCursor.isNull() && blockEndCursor.position() > blockStartCursor.position()) {
|
|
||||||
blockStartCursor = m_document->textDocument()->find("{", blockStartCursor, QTextDocument::FindBackward);
|
|
||||||
blockEndCursor = m_document->textDocument()->find("}", blockEndCursor, QTextDocument::FindBackward);
|
|
||||||
}
|
|
||||||
QString className = blockStartCursor.block().text();
|
|
||||||
className.remove(QRegExp(" *\\{"));
|
|
||||||
while (className.contains(" ")) {
|
|
||||||
className.remove(QRegExp(".* "));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're inside a class, add properties
|
// If we're inside a class, add properties
|
||||||
if (!className.isEmpty()) {
|
qDebug() << "Block name" << blockInfo.name;
|
||||||
foreach (const QString &s, m_classes.value(className)) {
|
|
||||||
entries.append(CompletionModel::Entry(s + ": ", s));
|
if (!blockInfo.name.isEmpty()) {
|
||||||
|
foreach (const QString &s, m_classes.value(blockInfo.name).properties) {
|
||||||
|
if (!blockInfo.properties.contains(s)) {
|
||||||
|
entries.append(CompletionModel::Entry(s + ": ", s, "property"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (const QString &s, m_classes.value(blockInfo.name).events) {
|
||||||
|
if (!blockInfo.properties.contains(s)) {
|
||||||
|
entries.append(CompletionModel::Entry(s + ": ", s, "event"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always append class names
|
// Always append class names
|
||||||
foreach (const QString &s, m_classes.keys()) {
|
foreach (const QString &s, m_classes.keys()) {
|
||||||
entries.append(CompletionModel::Entry(s + " {", s));
|
entries.append(CompletionModel::Entry(s + " {", s, "type", "", "}"));
|
||||||
|
}
|
||||||
|
// Always append attached class names
|
||||||
|
foreach (const QString &s, m_attachedClasses.keys()) {
|
||||||
|
entries.append(CompletionModel::Entry(s, s, "type"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add generic syntax
|
// Add generic qml syntax
|
||||||
foreach (const QString &s, m_genericSyntax.keys()) {
|
foreach (const QString &s, m_genericSyntax.keys()) {
|
||||||
entries.append(CompletionModel::Entry(m_genericSyntax.value(s), s));
|
entries.append(CompletionModel::Entry(m_genericSyntax.value(s), s, "keyword", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_model->update(entries);
|
m_model->update(entries);
|
||||||
@ -355,11 +432,12 @@ void CodeCompletion::update()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
m_model->update({});
|
m_model->update({});
|
||||||
m_proxy->setFilter(QString());
|
m_proxy->setFilter(QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position)
|
CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position) const
|
||||||
{
|
{
|
||||||
BlockInfo info;
|
BlockInfo info;
|
||||||
|
|
||||||
@ -374,6 +452,10 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position)
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info.start = blockStart.position();
|
||||||
|
info.end = m_document->textDocument()->find("}", position).position();
|
||||||
|
info.valid = true;
|
||||||
|
|
||||||
qDebug() << "Block strats at" << blockStart.position();
|
qDebug() << "Block strats at" << blockStart.position();
|
||||||
|
|
||||||
info.name = blockStart.block().text();
|
info.name = blockStart.block().text();
|
||||||
@ -382,13 +464,25 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position)
|
|||||||
info.name.remove(QRegExp(".* "));
|
info.name.remove(QRegExp(".* "));
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Block name:" << info.name;
|
int childBlocks = 0;
|
||||||
|
while (!blockStart.isNull() && blockStart.position() < position) {
|
||||||
while (blockStart.position() < position) {
|
QTextCursor tmp = m_document->textDocument()->find(QRegExp("[{}\n]"), blockStart);
|
||||||
qDebug() << "current pos:" << blockStart.position() << blockStart.block().text();
|
if (tmp.selectedText() == "{") {
|
||||||
QTextCursor tmp = m_document->textDocument()->find("\n", blockStart);
|
blockStart = tmp;
|
||||||
|
childBlocks++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (tmp.selectedText() == "}") {
|
||||||
|
blockStart = tmp;
|
||||||
|
childBlocks--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// \n
|
||||||
|
if (childBlocks > 0) { // Skip all stuff in child blocks
|
||||||
|
blockStart = tmp;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
foreach (const QString &statement, blockStart.block().text().split(";")) {
|
foreach (const QString &statement, blockStart.block().text().split(";")) {
|
||||||
qDebug() << "statement:" << statement;
|
|
||||||
QStringList parts = statement.split(":");
|
QStringList parts = statement.split(":");
|
||||||
if (parts.length() != 2) {
|
if (parts.length() != 2) {
|
||||||
continue;
|
continue;
|
||||||
@ -398,12 +492,88 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position)
|
|||||||
qDebug() << "inserting:" << propName << "->" << propValue;
|
qDebug() << "inserting:" << propName << "->" << propValue;
|
||||||
info.properties.insert(propName, propValue);
|
info.properties.insert(propName, propValue);
|
||||||
}
|
}
|
||||||
blockStart.movePosition(QTextCursor::NextBlock);
|
blockStart = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<CompletionModel::Entry> CodeCompletion::getIds() const
|
||||||
|
{
|
||||||
|
// Find all ids in the doc
|
||||||
|
QList<CompletionModel::Entry> entries;
|
||||||
|
QTextCursor tmp = QTextCursor(m_document->textDocument());
|
||||||
|
while (!tmp.atEnd()) {
|
||||||
|
tmp.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
|
||||||
|
tmp.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
||||||
|
QString word = tmp.selectedText();
|
||||||
|
if (word == "id") {
|
||||||
|
tmp.movePosition(QTextCursor::NextWord, QTextCursor::MoveAnchor);
|
||||||
|
tmp.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
||||||
|
QString idName = tmp.selectedText();
|
||||||
|
entries.append(CompletionModel::Entry(idName, idName, "id", ""));
|
||||||
|
}
|
||||||
|
tmp.movePosition(QTextCursor::NextWord);
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<QString, QString> CodeCompletion::getIdTypes() const
|
||||||
|
{
|
||||||
|
QHash<QString, QString> ret;
|
||||||
|
QTextCursor tmp = QTextCursor(m_document->textDocument());
|
||||||
|
while (!tmp.atEnd()) {
|
||||||
|
tmp.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
|
||||||
|
tmp.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
||||||
|
QString word = tmp.selectedText();
|
||||||
|
if (word == "id") {
|
||||||
|
tmp.movePosition(QTextCursor::NextWord, QTextCursor::MoveAnchor);
|
||||||
|
tmp.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
||||||
|
QString idName = tmp.selectedText();
|
||||||
|
BlockInfo info = getBlockInfo(tmp.position());
|
||||||
|
if (!info.name.isEmpty()) {
|
||||||
|
ret.insert(idName, info.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp.movePosition(QTextCursor::NextWord);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CodeCompletion::openingBlocksBefore(int position) const
|
||||||
|
{
|
||||||
|
int opening = 0;
|
||||||
|
int closing = 0;
|
||||||
|
QTextCursor tmp = m_cursor;
|
||||||
|
tmp.setPosition(position);
|
||||||
|
do {
|
||||||
|
tmp = m_document->textDocument()->find(QRegExp("[{}]"), tmp, QTextDocument::FindBackward);
|
||||||
|
if (tmp.selectedText() == "{")
|
||||||
|
opening++;
|
||||||
|
if (tmp.selectedText() == "}")
|
||||||
|
closing++;
|
||||||
|
} while (!tmp.isNull());
|
||||||
|
|
||||||
|
return opening - closing;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CodeCompletion::closingBlocksAfter(int position) const
|
||||||
|
{
|
||||||
|
int opening = 0;
|
||||||
|
int closing = 0;
|
||||||
|
QTextCursor tmp = m_cursor;
|
||||||
|
tmp.setPosition(position);
|
||||||
|
do {
|
||||||
|
tmp = m_document->textDocument()->find(QRegExp("[{}]"), tmp);
|
||||||
|
if (tmp.selectedText() == "{")
|
||||||
|
opening++;
|
||||||
|
if (tmp.selectedText() == "}")
|
||||||
|
closing++;
|
||||||
|
} while (!tmp.isNull());
|
||||||
|
|
||||||
|
return closing - opening;
|
||||||
|
}
|
||||||
|
|
||||||
void CodeCompletion::complete(int index)
|
void CodeCompletion::complete(int index)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= m_proxy->rowCount()) {
|
if (index < 0 || index >= m_proxy->rowCount()) {
|
||||||
@ -411,27 +581,26 @@ void CodeCompletion::complete(int index)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CompletionModel::Entry entry = m_proxy->get(index);
|
CompletionModel::Entry entry = m_proxy->get(index);
|
||||||
QString textToInsert = entry.text;
|
|
||||||
|
|
||||||
if (entry.addTrailingQuote) {
|
|
||||||
textToInsert.append("\"");
|
|
||||||
}
|
|
||||||
if (entry.addComment) {
|
|
||||||
textToInsert.append(" // " + entry.displayText);
|
|
||||||
}
|
|
||||||
// textToInsert.append("\n");
|
|
||||||
m_cursor.select(QTextCursor::WordUnderCursor);
|
m_cursor.select(QTextCursor::WordUnderCursor);
|
||||||
m_cursor.removeSelectedText();
|
m_cursor.removeSelectedText();
|
||||||
m_cursor.insertText(textToInsert);
|
qDebug() << "inserting:" << entry.text;
|
||||||
if (textToInsert.endsWith("{")) {
|
m_cursor.insertText(entry.text);
|
||||||
insertAfterCursor("}");
|
|
||||||
}
|
qDebug() << "inserting after cursor:" << entry.trailingText;
|
||||||
|
insertAfterCursor(entry.trailingText);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeCompletion::newLine()
|
void CodeCompletion::newLine()
|
||||||
{
|
{
|
||||||
qDebug() << "Newline" << m_cursor.position();
|
qDebug() << "Newline" << m_cursor.position();
|
||||||
QString line = m_cursor.block().text();
|
QString line = m_cursor.block().text();
|
||||||
|
|
||||||
|
if (line.endsWith("{") && openingBlocksBefore(m_cursor.position()) > closingBlocksAfter(m_cursor.position())) {
|
||||||
|
m_cursor.insertText("}");
|
||||||
|
m_cursor.movePosition(QTextCursor::PreviousCharacter);
|
||||||
|
}
|
||||||
|
|
||||||
QString trimmedLine = line;
|
QString trimmedLine = line;
|
||||||
trimmedLine.remove(QRegExp("^[ ]+"));
|
trimmedLine.remove(QRegExp("^[ ]+"));
|
||||||
int indent = line.length() - trimmedLine.length();
|
int indent = line.length() - trimmedLine.length();
|
||||||
@ -511,23 +680,43 @@ void CodeCompletion::closeBlock()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CodeCompletion::insertBeforeCursor(const QString &text)
|
||||||
|
{
|
||||||
|
m_cursor.insertText(text);
|
||||||
|
}
|
||||||
|
|
||||||
void CodeCompletion::insertAfterCursor(const QString &text)
|
void CodeCompletion::insertAfterCursor(const QString &text)
|
||||||
{
|
{
|
||||||
m_cursor.insertText(text);
|
m_cursor.insertText(text);
|
||||||
m_cursor.movePosition(QTextCursor::PreviousCharacter);
|
m_cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, text.length());
|
||||||
emit cursorPositionChanged();
|
emit cursorPositionChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
void CodeCompletion::moveCursor(CodeCompletion::MoveOperation moveOperation, int count)
|
||||||
void CodeCompletion::registerType(const QString &qmlName)
|
|
||||||
{
|
{
|
||||||
QMetaObject metaObject = T::staticMetaObject;
|
switch (moveOperation) {
|
||||||
QStringList properties;
|
case MoveOperationPreviousLine:
|
||||||
for (int i = 0; i < metaObject.propertyCount(); i++) {
|
m_cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor, count);
|
||||||
qDebug() << "Adding prop" << metaObject.property(i).name() << metaObject.property(i).type();
|
emit cursorPositionChanged();
|
||||||
if (metaObject.property(i).isWritable()) {
|
return;
|
||||||
properties.append(metaObject.property(i).name());
|
case MoveOperationNextLine:
|
||||||
}
|
m_cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, count);
|
||||||
|
emit cursorPositionChanged();
|
||||||
|
return;
|
||||||
|
case MoveOperationPreviousWord: {
|
||||||
|
// We're not using the cursors next/previos word because we want camelCase word fragments
|
||||||
|
QTextCursor tmp = m_document->textDocument()->find(QRegExp("[A-Z\\.:\"'\\(\\)\\[\\]^ ]"), m_cursor.position() - 1, QTextDocument::FindBackward);
|
||||||
|
qWarning() << "found at" << tmp.position() << "starting at" << m_cursor.position();
|
||||||
|
m_cursor.setPosition(tmp.position());
|
||||||
|
emit cursorPositionChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case MoveOperationNextWord: {
|
||||||
|
// We're not using the cursors next/previos word because we want camelCase word fragments
|
||||||
|
QTextCursor tmp = m_document->textDocument()->find(QRegExp("[A-Z\\.:\"'\\(\\)\\[\\]$ ]"), m_cursor.position() + 1);
|
||||||
|
m_cursor.setPosition(tmp.position() - 1);
|
||||||
|
emit cursorPositionChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_classes.insert(qmlName, properties);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,9 +6,9 @@
|
|||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
|
||||||
|
#include "completionmodel.h"
|
||||||
|
|
||||||
class Engine;
|
class Engine;
|
||||||
class CompletionModel;
|
|
||||||
class CompletionProxyModel;
|
|
||||||
|
|
||||||
class CodeCompletion: public QObject
|
class CodeCompletion: public QObject
|
||||||
{
|
{
|
||||||
@ -20,6 +20,14 @@ class CodeCompletion: public QObject
|
|||||||
Q_PROPERTY(QString currentWord READ currentWord NOTIFY currentWordChanged)
|
Q_PROPERTY(QString currentWord READ currentWord NOTIFY currentWordChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum MoveOperation {
|
||||||
|
MoveOperationPreviousLine,
|
||||||
|
MoveOperationNextLine,
|
||||||
|
MoveOperationPreviousWord,
|
||||||
|
MoveOperationNextWord,
|
||||||
|
};
|
||||||
|
Q_ENUM(MoveOperation)
|
||||||
|
|
||||||
CodeCompletion(QObject *parent = nullptr);
|
CodeCompletion(QObject *parent = nullptr);
|
||||||
|
|
||||||
Engine* engine() const;
|
Engine* engine() const;
|
||||||
@ -43,8 +51,11 @@ public slots:
|
|||||||
void indent(int from, int to);
|
void indent(int from, int to);
|
||||||
void unindent(int from, int to);
|
void unindent(int from, int to);
|
||||||
void closeBlock();
|
void closeBlock();
|
||||||
|
void insertBeforeCursor(const QString &text);
|
||||||
void insertAfterCursor(const QString &text);
|
void insertAfterCursor(const QString &text);
|
||||||
|
|
||||||
|
void moveCursor(MoveOperation moveOperation, int count = 1);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void engineChanged();
|
void engineChanged();
|
||||||
void documentChanged();
|
void documentChanged();
|
||||||
@ -52,14 +63,31 @@ signals:
|
|||||||
void currentWordChanged();
|
void currentWordChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct BlockInfo {
|
class BlockInfo {
|
||||||
|
public:
|
||||||
|
bool valid = false;
|
||||||
QString name;
|
QString name;
|
||||||
QHash<QString, QString> properties;
|
QHash<QString, QString> properties;
|
||||||
|
int start = -1;
|
||||||
|
int end = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
BlockInfo getBlockInfo(int postition);
|
class ClassInfo {
|
||||||
|
public:
|
||||||
|
ClassInfo(const QString &name = QString(), const QStringList &properties = QStringList(), const QStringList &methods = QStringList(), const QStringList &events = QStringList()):
|
||||||
|
name(name), properties(properties), methods(methods), events(events) {}
|
||||||
|
QString name;
|
||||||
|
QStringList properties;
|
||||||
|
QStringList methods;
|
||||||
|
QStringList events;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T> void registerType(const QString &qmlName);
|
BlockInfo getBlockInfo(int postition) const;
|
||||||
|
QList<CompletionModel::Entry> getIds() const;
|
||||||
|
QHash<QString, QString> getIdTypes() const;
|
||||||
|
|
||||||
|
int openingBlocksBefore(int position) const;
|
||||||
|
int closingBlocksAfter(int position) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Engine *m_engine = nullptr;
|
Engine *m_engine = nullptr;
|
||||||
@ -69,8 +97,11 @@ private:
|
|||||||
|
|
||||||
QTextCursor m_cursor;
|
QTextCursor m_cursor;
|
||||||
|
|
||||||
QHash<QString, QStringList> m_classes;
|
QHash<QString, ClassInfo> m_classes;
|
||||||
|
QHash<QString, ClassInfo> m_attachedClasses;
|
||||||
|
QHash<QString, ClassInfo> m_jsClasses;
|
||||||
QHash<QString, QString> m_genericSyntax;
|
QHash<QString, QString> m_genericSyntax;
|
||||||
|
QHash<QString, QString> m_genericJsSyntax;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,8 @@ QHash<int, QByteArray> CompletionModel::roleNames() const
|
|||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
roles.insert(Qt::UserRole, "text");
|
roles.insert(Qt::UserRole, "text");
|
||||||
roles.insert(Qt::DisplayRole, "displayText");
|
roles.insert(Qt::DisplayRole, "displayText");
|
||||||
|
roles.insert(Qt::DecorationRole, "decoration");
|
||||||
|
roles.insert(Qt::DecorationPropertyRole, "decorationProperty");
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,6 +30,10 @@ QVariant CompletionModel::data(const QModelIndex &index, int role) const
|
|||||||
return m_list.at(index.row()).text;
|
return m_list.at(index.row()).text;
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
return m_list.at(index.row()).displayText;
|
return m_list.at(index.row()).displayText;
|
||||||
|
case Qt::DecorationRole:
|
||||||
|
return m_list.at(index.row()).decoration;
|
||||||
|
case Qt::DecorationPropertyRole:
|
||||||
|
return m_list.at(index.row()).decorationProperty;
|
||||||
}
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
@ -72,7 +78,6 @@ QString CompletionProxyModel::filter() const
|
|||||||
void CompletionProxyModel::setFilter(const QString &filter)
|
void CompletionProxyModel::setFilter(const QString &filter)
|
||||||
{
|
{
|
||||||
if (m_filter != filter) {
|
if (m_filter != filter) {
|
||||||
qDebug() << "Setting filter" << filter;
|
|
||||||
m_filter = filter;
|
m_filter = filter;
|
||||||
emit filterChanged();
|
emit filterChanged();
|
||||||
invalidateFilter();
|
invalidateFilter();
|
||||||
@ -90,3 +95,20 @@ bool CompletionProxyModel::filterAcceptsRow(int source_row, const QModelIndex &)
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CompletionProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
|
||||||
|
{
|
||||||
|
CompletionModel::Entry left = m_model->get(source_left.row());
|
||||||
|
CompletionModel::Entry right = m_model->get(source_right.row());
|
||||||
|
|
||||||
|
static QStringList ordering = {"property", "method", "event", "type", "keyword" };
|
||||||
|
|
||||||
|
int leftOrder = ordering.indexOf(left.decoration);
|
||||||
|
int rightOrder = ordering.indexOf(right.decoration);
|
||||||
|
|
||||||
|
if (leftOrder != rightOrder) {
|
||||||
|
return leftOrder < rightOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
return left.displayText < right.displayText;
|
||||||
|
}
|
||||||
|
|||||||
@ -11,13 +11,14 @@ class CompletionModel: public QAbstractListModel
|
|||||||
public:
|
public:
|
||||||
class Entry {
|
class Entry {
|
||||||
public:
|
public:
|
||||||
Entry(const QString &text, const QString &displayText, bool addTrailingQuote = false, bool addComment = false)
|
Entry(const QString &text, const QString &displayText, const QString &decoration, const QString &decorationProperty = QString(), const QString &trailingText = QString())
|
||||||
: text(text), displayText(displayText), addTrailingQuote(addTrailingQuote), addComment(addComment) {}
|
: text(text), displayText(displayText), decoration(decoration), decorationProperty(decorationProperty), trailingText(trailingText) {}
|
||||||
Entry(const QString &text): text(text), displayText(text) {}
|
Entry(const QString &text): text(text), displayText(text) {}
|
||||||
QString text;
|
QString text;
|
||||||
QString displayText;
|
QString displayText;
|
||||||
bool addTrailingQuote = false;
|
QString decoration;
|
||||||
bool addComment = false;
|
QString decorationProperty;
|
||||||
|
QString trailingText;
|
||||||
};
|
};
|
||||||
|
|
||||||
CompletionModel(QObject *parent = nullptr);
|
CompletionModel(QObject *parent = nullptr);
|
||||||
@ -52,6 +53,8 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
bool filterAcceptsRow(int source_row, const QModelIndex &/*source_parent*/) const override;
|
bool filterAcceptsRow(int source_row, const QModelIndex &/*source_parent*/) const override;
|
||||||
|
|
||||||
|
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void countChanged();
|
void countChanged();
|
||||||
void filterChanged();
|
void filterChanged();
|
||||||
|
|||||||
@ -28,20 +28,27 @@ Scripts *ScriptManager::scripts() const
|
|||||||
return m_scripts;
|
return m_scripts;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ScriptManager::addScript(const QString &content)
|
int ScriptManager::addScript(const QString &name, const QString &content)
|
||||||
{
|
{
|
||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
params.insert("name", "Test");
|
params.insert("name", name);
|
||||||
params.insert("content", content);
|
params.insert("content", content);
|
||||||
return m_client->sendCommand("Scripts.AddScript", params, this, "onScriptAdded");
|
return m_client->sendCommand("Scripts.AddScript", params, this, "onScriptAdded");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ScriptManager::renameScript(const QUuid &id, const QString &name)
|
||||||
|
{
|
||||||
|
QVariantMap params;
|
||||||
|
params.insert("id", id);
|
||||||
|
params.insert("name", name);
|
||||||
|
return m_client->sendCommand("Scripts.EditScript", params, this, "onScriptRenamed");
|
||||||
|
}
|
||||||
|
|
||||||
int ScriptManager::editScript(const QUuid &id, const QString &content)
|
int ScriptManager::editScript(const QUuid &id, const QString &content)
|
||||||
{
|
{
|
||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
params.insert("id", id);
|
params.insert("id", id);
|
||||||
params.insert("content", content);
|
params.insert("content", content);
|
||||||
qDebug() << "Calling EditScript" << content;
|
|
||||||
return m_client->sendCommand("Scripts.EditScript", params, this, "onScriptEdited");
|
return m_client->sendCommand("Scripts.EditScript", params, this, "onScriptEdited");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,29 +68,24 @@ int ScriptManager::fetchScript(const QUuid &id)
|
|||||||
|
|
||||||
void ScriptManager::onScriptsFetched(const QVariantMap ¶ms)
|
void ScriptManager::onScriptsFetched(const QVariantMap ¶ms)
|
||||||
{
|
{
|
||||||
qDebug() << "scripts fetched" << params;
|
|
||||||
foreach (const QVariant &variant, params.value("params").toMap().value("scripts").toList()) {
|
foreach (const QVariant &variant, params.value("params").toMap().value("scripts").toList()) {
|
||||||
qDebug() << "script" << variant.toMap().value("id").toUuid();
|
|
||||||
QUuid id = variant.toMap().value("id").toUuid();
|
QUuid id = variant.toMap().value("id").toUuid();
|
||||||
Script *script = new Script(id);
|
Script *script = new Script(id);
|
||||||
script->setName(variant.toMap().value("name").toString());
|
script->setName(variant.toMap().value("name").toString());
|
||||||
m_scripts->addScript(script);
|
m_scripts->addScript(script);
|
||||||
qDebug() << "Script added";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptManager::onScriptFetched(const QVariantMap ¶ms)
|
void ScriptManager::onScriptFetched(const QVariantMap ¶ms)
|
||||||
{
|
{
|
||||||
qDebug() << "Script fetched" << params;
|
emit fetchScriptReply(params.value("id").toInt(),
|
||||||
emit scriptFetched(params.value("id").toInt(),
|
|
||||||
params.value("params").toMap().value("scriptError").toString(),
|
params.value("params").toMap().value("scriptError").toString(),
|
||||||
params.value("params").toMap().value("content").toString());
|
params.value("params").toMap().value("content").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptManager::onScriptAdded(const QVariantMap ¶ms)
|
void ScriptManager::onScriptAdded(const QVariantMap ¶ms)
|
||||||
{
|
{
|
||||||
qDebug() << "Script added" << params;
|
emit addScriptReply(params.value("id").toInt(),
|
||||||
emit scriptAdded(params.value("id").toInt(),
|
|
||||||
params.value("params").toMap().value("scriptError").toString(),
|
params.value("params").toMap().value("scriptError").toString(),
|
||||||
params.value("params").toMap().value("script").toMap().value("id").toUuid(),
|
params.value("params").toMap().value("script").toMap().value("id").toUuid(),
|
||||||
params.value("params").toMap().value("errors").toStringList());
|
params.value("params").toMap().value("errors").toStringList());
|
||||||
@ -92,25 +94,50 @@ void ScriptManager::onScriptAdded(const QVariantMap ¶ms)
|
|||||||
|
|
||||||
void ScriptManager::onScriptEdited(const QVariantMap ¶ms)
|
void ScriptManager::onScriptEdited(const QVariantMap ¶ms)
|
||||||
{
|
{
|
||||||
qDebug() << "Script edited" << params;
|
emit editScriptReply(params.value("id").toInt(),
|
||||||
// emit scriptAdded(params.value("id").toInt(), params.value("script").toMap().value("id").toUuid());
|
|
||||||
emit scriptEdited(params.value("id").toInt(),
|
|
||||||
params.value("params").toMap().value("scriptError").toString(),
|
params.value("params").toMap().value("scriptError").toString(),
|
||||||
params.value("params").toMap().value("errors").toStringList());
|
params.value("params").toMap().value("errors").toStringList());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptManager::onScriptRenamed(const QVariantMap ¶ms)
|
||||||
|
{
|
||||||
|
emit renameScriptReply(params.value("id").toInt(), params.value("params").toMap().value("scriptError").toString());
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptManager::onScriptRemoved(const QVariantMap ¶ms)
|
void ScriptManager::onScriptRemoved(const QVariantMap ¶ms)
|
||||||
{
|
{
|
||||||
emit scriptRemoved(params.value("id").toInt(), params.value("params").toMap().value("scriptError").toString());
|
emit removeScriptReply(params.value("id").toInt(), params.value("params").toMap().value("scriptError").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptManager::onNotificationReceived(const QVariantMap ¶ms)
|
void ScriptManager::onNotificationReceived(const QVariantMap ¶ms)
|
||||||
{
|
{
|
||||||
qDebug() << "noticication" << params;
|
qDebug() << "noticication" << params.value("notification").toString();
|
||||||
if (params.value("notification").toString() == "Scripts.ScriptLogMessage") {
|
if (params.value("notification").toString() == "Scripts.ScriptLogMessage") {
|
||||||
emit scriptMessage(params.value("params").toMap().value("scriptId").toUuid(),
|
emit scriptMessage(params.value("params").toMap().value("scriptId").toUuid(),
|
||||||
params.value("params").toMap().value("type").toString(),
|
params.value("params").toMap().value("type").toString(),
|
||||||
params.value("params").toMap().value("message").toString());
|
params.value("params").toMap().value("message").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (params.value("notification").toString() == "Scripts.ScriptAdded") {
|
||||||
|
QVariantMap scriptMap = params.value("params").toMap().value("script").toMap();
|
||||||
|
Script *script = new Script(scriptMap.value("id").toUuid());
|
||||||
|
script->setName(scriptMap.value("name").toString());
|
||||||
|
m_scripts->addScript(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (params.value("notification").toString() == "Scripts.ScriptRemoved") {
|
||||||
|
QUuid id = params.value("params").toMap().value("scriptId").toUuid();
|
||||||
|
m_scripts->removeScript(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (params.value("notification").toString() == "Scripts.ScriptChanged") {
|
||||||
|
QUuid id = params.value("params").toMap().value("scriptId").toUuid();
|
||||||
|
QString name = params.value("params").toMap().value("name").toString();
|
||||||
|
m_scripts->getScript(id)->setName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
qWarning() << "Unhandled notification" << params.value("notification").toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,16 +22,18 @@ public:
|
|||||||
Scripts *scripts() const;
|
Scripts *scripts() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
int addScript(const QString &content);
|
int addScript(const QString &name, const QString &content);
|
||||||
|
int renameScript(const QUuid &id, const QString &name);
|
||||||
int editScript(const QUuid &id, const QString &content);
|
int editScript(const QUuid &id, const QString &content);
|
||||||
int removeScript(const QUuid &id);
|
int removeScript(const QUuid &id);
|
||||||
int fetchScript(const QUuid &id);
|
int fetchScript(const QUuid &id);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void scriptAdded(int id, const QString &scriptError, const QUuid &scriptId, const QStringList &errors);
|
void addScriptReply(int id, const QString &scriptError, const QUuid &scriptId, const QStringList &errors);
|
||||||
void scriptEdited(int id, const QString &scriptError, const QStringList &errors);
|
void editScriptReply(int id, const QString &scriptError, const QStringList &errors);
|
||||||
void scriptRemoved(int id, const QString &scriptError);
|
void renameScriptReply(int id, const QString &scriptError);
|
||||||
void scriptFetched(int id, const QString &scriptError, const QString &content);
|
void removeScriptReply(int id, const QString &scriptError);
|
||||||
|
void fetchScriptReply(int id, const QString &scriptError, const QString &content);
|
||||||
|
|
||||||
void scriptMessage(const QUuid &scriptId, const QString &type, const QString &message);
|
void scriptMessage(const QUuid &scriptId, const QString &type, const QString &message);
|
||||||
|
|
||||||
@ -40,6 +42,7 @@ private slots:
|
|||||||
void onScriptFetched(const QVariantMap ¶ms);
|
void onScriptFetched(const QVariantMap ¶ms);
|
||||||
void onScriptAdded(const QVariantMap ¶ms);
|
void onScriptAdded(const QVariantMap ¶ms);
|
||||||
void onScriptEdited(const QVariantMap ¶ms);
|
void onScriptEdited(const QVariantMap ¶ms);
|
||||||
|
void onScriptRenamed(const QVariantMap ¶ms);
|
||||||
void onScriptRemoved(const QVariantMap ¶ms);
|
void onScriptRemoved(const QVariantMap ¶ms);
|
||||||
|
|
||||||
void onNotificationReceived(const QVariantMap ¶ms);
|
void onNotificationReceived(const QVariantMap ¶ms);
|
||||||
|
|||||||
@ -49,6 +49,24 @@ void Scripts::addScript(Script *script)
|
|||||||
m_list.append(script);
|
m_list.append(script);
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
emit countChanged();
|
emit countChanged();
|
||||||
|
|
||||||
|
connect(script, &Script::nameChanged, this, [this, script](){
|
||||||
|
int idx = m_list.indexOf(script);
|
||||||
|
if (idx < 0) return;
|
||||||
|
emit dataChanged(index(idx), index(idx), {RoleName});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scripts::removeScript(const QUuid &id)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_list.count(); i++) {
|
||||||
|
if (m_list.at(i)->id() == id) {
|
||||||
|
beginRemoveRows(QModelIndex(), i, i);
|
||||||
|
m_list.takeAt(i)->deleteLater();
|
||||||
|
endRemoveRows();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Script* Scripts::get(int index) const
|
Script* Scripts::get(int index) const
|
||||||
|
|||||||
@ -24,6 +24,7 @@ public:
|
|||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
void addScript(Script *script);
|
void addScript(Script *script);
|
||||||
|
void removeScript(const QUuid &id);
|
||||||
|
|
||||||
Q_INVOKABLE Script *get(int index) const;
|
Q_INVOKABLE Script *get(int index) const;
|
||||||
Q_INVOKABLE Script *getScript(const QUuid &scriptId);
|
Q_INVOKABLE Script *getScript(const QUuid &scriptId);
|
||||||
|
|||||||
@ -214,5 +214,6 @@
|
|||||||
<file>ui/images/browser/MediaBrowserIconDeezer.svg</file>
|
<file>ui/images/browser/MediaBrowserIconDeezer.svg</file>
|
||||||
<file>ui/images/view-grid-symbolic.svg</file>
|
<file>ui/images/view-grid-symbolic.svg</file>
|
||||||
<file>ui/images/script.svg</file>
|
<file>ui/images/script.svg</file>
|
||||||
|
<file>ui/images/save.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@ -58,6 +58,7 @@ Item {
|
|||||||
font.pixelSize: app.mediumFont
|
font.pixelSize: app.mediumFont
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
text: root.text
|
text: root.text
|
||||||
|
visible: text.length > 0
|
||||||
color: app.headerForegroundColor
|
color: app.headerForegroundColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
170
nymea-app/ui/images/save.svg
Normal file
170
nymea-app/ui/images/save.svg
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="96"
|
||||||
|
height="96"
|
||||||
|
id="svg4874"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91+devel r"
|
||||||
|
viewBox="0 0 96 96.000001"
|
||||||
|
sodipodi:docname="save.svg">
|
||||||
|
<defs
|
||||||
|
id="defs4876" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="11.258999"
|
||||||
|
inkscape:cx="40.139054"
|
||||||
|
inkscape:cy="22.294719"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="g4780"
|
||||||
|
showgrid="true"
|
||||||
|
showborder="true"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:bbox-paths="true"
|
||||||
|
inkscape:bbox-nodes="true"
|
||||||
|
inkscape:snap-bbox-edge-midpoints="true"
|
||||||
|
inkscape:snap-bbox-midpoints="true"
|
||||||
|
inkscape:object-paths="true"
|
||||||
|
inkscape:snap-intersection-paths="true"
|
||||||
|
inkscape:object-nodes="true"
|
||||||
|
inkscape:snap-smooth-nodes="true"
|
||||||
|
inkscape:snap-midpoints="true"
|
||||||
|
inkscape:snap-object-midpoints="true"
|
||||||
|
inkscape:snap-center="true"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid5451"
|
||||||
|
empspacing="8" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="1,0"
|
||||||
|
position="8,-8.0000001"
|
||||||
|
id="guide4063" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="1,0"
|
||||||
|
position="4,-8.0000001"
|
||||||
|
id="guide4065" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="0,1"
|
||||||
|
position="-8,88.000001"
|
||||||
|
id="guide4067" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="0,1"
|
||||||
|
position="-8,92.000001"
|
||||||
|
id="guide4069" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="0,1"
|
||||||
|
position="104,4"
|
||||||
|
id="guide4071" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="0,1"
|
||||||
|
position="-5,8.0000001"
|
||||||
|
id="guide4073" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="1,0"
|
||||||
|
position="92,-8.0000001"
|
||||||
|
id="guide4075" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="1,0"
|
||||||
|
position="88,-8.0000001"
|
||||||
|
id="guide4077" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="0,1"
|
||||||
|
position="-8,84.000001"
|
||||||
|
id="guide4074" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="1,0"
|
||||||
|
position="12,-8.0000001"
|
||||||
|
id="guide4076" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="0,1"
|
||||||
|
position="-5,12"
|
||||||
|
id="guide4078" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="1,0"
|
||||||
|
position="84,-9.0000001"
|
||||||
|
id="guide4080" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="48,-8.0000001"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide4170" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="-8,48"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide4172" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata4879">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(67.857146,-78.50504)">
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
|
||||||
|
id="g4845"
|
||||||
|
style="display:inline">
|
||||||
|
<g
|
||||||
|
inkscape:export-ydpi="90"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-filename="next01.png"
|
||||||
|
transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)"
|
||||||
|
id="g4778"
|
||||||
|
inkscape:label="Layer 1">
|
||||||
|
<g
|
||||||
|
transform="matrix(-1,0,0,1,575.99999,611)"
|
||||||
|
id="g4780"
|
||||||
|
style="display:inline">
|
||||||
|
<path
|
||||||
|
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.99999952;marker:none;enable-background:accumulate"
|
||||||
|
d="m 436.00166,403.36222 -32.01266,0 0,13.625 c 0,0 -24.94685,-11.15391 -42.01662,-23.62305 17.06977,-12.46913 42.01662,-23.62695 42.01662,-23.62695 l 0,13.625 32.01266,0 z m -4.00158,-4 0,-12 -32.01266,0 0,-11.21289 c -5.98966,2.82047 -18.03813,8.81417 -30.68011,17.21484 12.64206,8.40027 24.69078,14.39149 30.68011,17.21094 l 0,-11.21289 z"
|
||||||
|
id="path4179"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccccccccccccccc" />
|
||||||
|
<rect
|
||||||
|
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"
|
||||||
|
id="rect4782"
|
||||||
|
width="96.037987"
|
||||||
|
height="96"
|
||||||
|
x="-438.00244"
|
||||||
|
y="345.36221"
|
||||||
|
transform="scale(-1,1)" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079107;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:8.00158233, 8.00158233;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
|
||||||
|
d="m 355.97,425.36222 -12.00475,0 0,-64 12.00475,0 z m -4.00158,-52 0,-8 -4.00159,0 0,8 z"
|
||||||
|
id="rect4181"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccccccccc" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.6 KiB |
@ -1,4 +1,4 @@
|
|||||||
import QtQuick 2.0
|
import QtQuick 2.4
|
||||||
import QtQuick.Controls 2.2
|
import QtQuick.Controls 2.2
|
||||||
import Nymea 1.0
|
import Nymea 1.0
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
@ -12,16 +12,15 @@ Page {
|
|||||||
property alias scriptId: d.scriptId
|
property alias scriptId: d.scriptId
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (scriptId !== undefined) {
|
if (scriptId !== undefined) {;
|
||||||
d.callId = engine.scriptManager.fetchScript(scriptId);
|
d.callId = engine.scriptManager.fetchScript(scriptId);
|
||||||
} else {
|
} else {
|
||||||
scriptEdit.text = "import QtQuick 2.0\nimport nymea 1.0\n\nItem {\n \n}\n"
|
scriptEdit.text = "import QtQuick 2.0\nimport nymea 1.0\n\nItem {\n \n}\n"
|
||||||
d.callId = engine.scriptManager.addScript(scriptEdit.text);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
header: NymeaHeader {
|
header: NymeaHeader {
|
||||||
text: qsTr("Script editor")
|
|
||||||
onBackPressed: {
|
onBackPressed: {
|
||||||
if (scriptEdit.text == d.oldContent) {
|
if (scriptEdit.text == d.oldContent) {
|
||||||
pageStack.pop()
|
pageStack.pop()
|
||||||
@ -37,17 +36,34 @@ Page {
|
|||||||
pageStack.pop();
|
pageStack.pop();
|
||||||
});
|
});
|
||||||
popup.open();
|
popup.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: nameTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: d.script ? d.script.name : ""
|
||||||
|
placeholderText: qsTr("Script name")
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderButton {
|
HeaderButton {
|
||||||
imageSource: "../images/media-playback-start.svg"
|
imageSource: "../images/save.svg"
|
||||||
|
enabled: d.script.name !== nameTextField.text || d.oldContent !== scriptEdit.text
|
||||||
|
color: enabled ? app.accentColor : keyColor
|
||||||
|
hoverEnabled: true
|
||||||
|
ToolTip.text: qsTr("Deploy script")
|
||||||
|
ToolTip.visible: hovered
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!d.scriptId) {
|
if (!d.scriptId) {
|
||||||
d.callId = engine.scriptManager.addScript(scriptEdit.text)
|
d.callId = engine.scriptManager.addScript(nameTextField.text, scriptEdit.text);
|
||||||
} else {
|
} else {
|
||||||
print("editing script", d.scriptId, scriptEdit.text)
|
print("editing script", d.scriptId)
|
||||||
d.callId = engine.scriptManager.editScript(d.scriptId, scriptEdit.text)
|
if (d.script.name != nameTextField.text) {
|
||||||
|
engine.scriptManager.renameScript(d.scriptId, nameTextField.text)
|
||||||
|
}
|
||||||
|
if (d.oldContent != scriptEdit.text) {
|
||||||
|
d.callId = engine.scriptManager.editScript(d.scriptId, scriptEdit.text)
|
||||||
|
print("called edit", d.callId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,32 +71,50 @@ Page {
|
|||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: d
|
id: d
|
||||||
property int callId
|
property int callId: -1
|
||||||
property var scriptId
|
property var scriptId
|
||||||
property string oldContent
|
property string oldContent
|
||||||
|
|
||||||
|
property Script script: engine.scriptManager.scripts.getScript(d.scriptId)
|
||||||
|
}
|
||||||
|
|
||||||
|
FontMetrics {
|
||||||
|
id: fontMetrics
|
||||||
|
font: scriptEdit.font
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: engine.scriptManager
|
target: engine.scriptManager
|
||||||
onScriptAdded: {
|
onAddScriptReply: {
|
||||||
if (id == d.callId) {
|
if (id == d.callId) {
|
||||||
|
d.callId = -1;
|
||||||
if (scriptError == "ScriptErrorNoError") {
|
if (scriptError == "ScriptErrorNoError") {
|
||||||
d.scriptId = scriptId;
|
d.scriptId = scriptId;
|
||||||
}
|
}
|
||||||
errorModel.update(errors);
|
errorModel.update(errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onScriptEdited: {
|
onEditScriptReply: {
|
||||||
|
print("edit reply", id, d.callId)
|
||||||
if (id == d.callId) {
|
if (id == d.callId) {
|
||||||
|
d.oldContent = scriptEdit.text;
|
||||||
|
d.callId = -1;
|
||||||
errorModel.update(errors)
|
errorModel.update(errors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onScriptFetched: {
|
onFetchScriptReply: {
|
||||||
if (id == d.callId && scriptError == "ScriptErrorNoError") {
|
if (id == d.callId && scriptError == "ScriptErrorNoError") {
|
||||||
|
d.callId = -1;
|
||||||
scriptEdit.text = content;
|
scriptEdit.text = content;
|
||||||
d.oldContent = content;
|
d.oldContent = content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onRenameScriptReply: {
|
||||||
|
if (id == d.callId) {
|
||||||
|
d.callId = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onScriptMessage: {
|
onScriptMessage: {
|
||||||
if (scriptId !== d.scriptId) {
|
if (scriptId !== d.scriptId) {
|
||||||
return;
|
return;
|
||||||
@ -91,6 +125,7 @@ Page {
|
|||||||
|
|
||||||
// TODO: Make this a SplitView when we can use Qt 5.13
|
// TODO: Make this a SplitView when we can use Qt 5.13
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Flickable {
|
Flickable {
|
||||||
@ -98,6 +133,7 @@ Page {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
clip: true
|
clip: true
|
||||||
|
interactive: !completionBox.visible
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar { policy: ScrollBar.AlwaysOn }
|
ScrollBar.vertical: ScrollBar { policy: ScrollBar.AlwaysOn }
|
||||||
@ -140,6 +176,21 @@ Page {
|
|||||||
completionBox.show();
|
completionBox.show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case Qt.Key_PageUp:
|
||||||
|
var oldSelectionStart = scriptEdit.selectionStart;
|
||||||
|
completion.moveCursor(CodeCompletion.MoveOperationPreviousLine, scriptFlickable.height / (fontMetrics.lineSpacing + 2));
|
||||||
|
if (event.modifiers & Qt.ShiftModifier) {
|
||||||
|
scriptEdit.select(oldSelectionStart, scriptEdit.cursorPosition)
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case Qt.Key_PageDown:
|
||||||
|
var oldSelectionStart = scriptEdit.selectionStart;
|
||||||
|
completion.moveCursor(CodeCompletion.MoveOperationNextLine, scriptFlickable.height / (fontMetrics.lineSpacing + 2));
|
||||||
|
if (event.modifiers & Qt.ShiftModifier) {
|
||||||
|
scriptEdit.select(oldSelectionStart, scriptEdit.cursorPosition)
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +212,11 @@ Page {
|
|||||||
completion.unindent(selectionStart, selectionEnd);
|
completion.unindent(selectionStart, selectionEnd);
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
return;
|
return;
|
||||||
|
case Qt.Key_Period:
|
||||||
|
completion.insertBeforeCursor(".");
|
||||||
|
completionBox.show();
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Things to do only when we're autocompleting
|
// Things to do only when we're autocompleting
|
||||||
@ -188,15 +243,6 @@ Page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletionBox {
|
|
||||||
id: completionBox
|
|
||||||
model: completion.model
|
|
||||||
textArea: scriptEdit
|
|
||||||
onComplete: {
|
|
||||||
completion.complete(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,10 +330,29 @@ Page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompletionBox {
|
||||||
|
id: completionBox
|
||||||
|
property var editorPosition: scriptFlickable.mapToItem(root, 0, 0)
|
||||||
|
property int scrollOffsetX: scriptFlickable.contentX + scriptFlickable.originX
|
||||||
|
property int scrollOffsetY: scriptFlickable.contentY + scriptFlickable.originY
|
||||||
|
property int cursorXOnPage: scriptEdit.cursorRectangle.x + editorPosition.x - scrollOffsetX
|
||||||
|
property int cursorYOnPage: scriptEdit.cursorRectangle.y + editorPosition.y - scrollOffsetY
|
||||||
|
property int cursorHeight: scriptEdit.cursorRectangle.height
|
||||||
|
x: cursorXOnPage - Math.max(0, cursorXOnPage + width - root.width)
|
||||||
|
y: cursorYOnPage + cursorHeight + height < content.height ?
|
||||||
|
cursorYOnPage + cursorHeight
|
||||||
|
: cursorYOnPage - height
|
||||||
|
|
||||||
|
model: completion.model
|
||||||
|
textArea: scriptEdit
|
||||||
|
font: scriptEdit.font
|
||||||
|
onComplete: {
|
||||||
|
completion.complete(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ScriptSyntaxHighlighter {
|
ScriptSyntaxHighlighter {
|
||||||
id: syntax
|
id: syntax
|
||||||
@ -302,4 +367,9 @@ Page {
|
|||||||
cursorPosition: scriptEdit.cursorPosition
|
cursorPosition: scriptEdit.cursorPosition
|
||||||
onCursorPositionChanged: scriptEdit.cursorPosition = cursorPosition
|
onCursorPositionChanged: scriptEdit.cursorPosition = cursorPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BusyOverlay {
|
||||||
|
shown: d.callId != -1
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ Page {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: engine.scriptManager
|
target: engine.scriptManager
|
||||||
onScriptRemoved: {
|
onRemovScriptReply: {
|
||||||
if (id == d.pendingAction) {
|
if (id == d.pendingAction) {
|
||||||
d.pendingAction = -1;
|
d.pendingAction = -1;
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ Page {
|
|||||||
delegate: NymeaListItemDelegate {
|
delegate: NymeaListItemDelegate {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: model.name
|
text: model.name
|
||||||
subText: model.id
|
iconName: "../images/script.svg"
|
||||||
canDelete: true
|
canDelete: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
pageStack.push("ScriptEditor.qml", {scriptId: model.id});
|
pageStack.push("ScriptEditor.qml", {scriptId: model.id});
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import QtQuick 2.2
|
import QtQuick 2.2
|
||||||
import QtQuick.Controls 2.2
|
import QtQuick.Controls 2.2
|
||||||
import Nymea 1.0
|
import Nymea 1.0
|
||||||
|
import QtQuick.Layouts 1.2
|
||||||
|
import "../../components"
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
@ -9,8 +11,6 @@ Rectangle {
|
|||||||
color: app.backgroundColor
|
color: app.backgroundColor
|
||||||
height: (Math.min(model.count, 10) * d.entryHeight) + (border.width * 2)
|
height: (Math.min(model.count, 10) * d.entryHeight) + (border.width * 2)
|
||||||
width: 200
|
width: 200
|
||||||
x: textArea.cursorRectangle.x
|
|
||||||
y: textArea.cursorRectangle.y + textArea.cursorRectangle.height
|
|
||||||
|
|
||||||
visible: model.count > 0 && !d.hidden
|
visible: model.count > 0 && !d.hidden
|
||||||
&& (model.filter.length >= 3 || d.manuallyInvoked)
|
&& (model.filter.length >= 3 || d.manuallyInvoked)
|
||||||
@ -46,8 +46,10 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
d.hidden = false;
|
if (root.model.count > 1) {
|
||||||
d.manuallyInvoked = true;
|
d.hidden = false;
|
||||||
|
d.manuallyInvoked = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
@ -65,13 +67,14 @@ Rectangle {
|
|||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: d
|
id: d
|
||||||
property int entryHeight: dummyLabel.font.pixelSize + 4
|
property int entryHeight: dummyLabel.font.pixelSize + 6
|
||||||
|
|
||||||
property int currentIndex: 0
|
property int currentIndex: 0
|
||||||
property bool hidden: false
|
property bool hidden: false
|
||||||
property bool manuallyInvoked: false
|
property bool manuallyInvoked: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: listView
|
id: listView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@ -86,15 +89,74 @@ Rectangle {
|
|||||||
height: d.entryHeight
|
height: d.entryHeight
|
||||||
width: parent.width
|
width: parent.width
|
||||||
color: index == root.currentIndex ? app.accentColor : "transparent"
|
color: index == root.currentIndex ? app.accentColor : "transparent"
|
||||||
Label {
|
RowLayout {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.fill: parent
|
||||||
anchors { left: parent.left; right: parent.right; margins: 4}
|
spacing: 0
|
||||||
text: model.displayText
|
|
||||||
color: app.foregroundColor
|
Item {
|
||||||
width: parent.width
|
Layout.preferredHeight: d.entryHeight
|
||||||
elide: Text.ElideRight
|
Layout.preferredWidth: height
|
||||||
font: root.font
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
height: root.font.pixelSize * .6
|
||||||
|
width: height
|
||||||
|
border.width: 1
|
||||||
|
border.color: "black"
|
||||||
|
visible: !entryIcon.visible
|
||||||
|
color: {
|
||||||
|
switch (model.decoration) {
|
||||||
|
case "type":
|
||||||
|
return "#55fc49";
|
||||||
|
case "keyword":
|
||||||
|
return "yellow";
|
||||||
|
case "property":
|
||||||
|
return "#ff5555";
|
||||||
|
case "method":
|
||||||
|
return "blue";
|
||||||
|
case "event":
|
||||||
|
return "magenta";
|
||||||
|
case "id":
|
||||||
|
return "turquise";
|
||||||
|
default:
|
||||||
|
return "transparent";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColorIcon {
|
||||||
|
id: entryIcon
|
||||||
|
height: root.font.pixelSize
|
||||||
|
width: height
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: name != ""
|
||||||
|
color: root.currentIndex == index ? app.backgroundColor : app.accentColor
|
||||||
|
name: {
|
||||||
|
switch (model.decoration) {
|
||||||
|
case "thing":
|
||||||
|
return app.interfacesToIcon(model.decorationProperty.split(","))
|
||||||
|
case "eventType":
|
||||||
|
return Qt.resolvedUrl("../../images/event.svg")
|
||||||
|
case "stateType":
|
||||||
|
return Qt.resolvedUrl("../../images/state.svg")
|
||||||
|
case "actionType":
|
||||||
|
return Qt.resolvedUrl("../../images/action.svg")
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: model.displayText
|
||||||
|
color: app.foregroundColor
|
||||||
|
width: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font: root.font
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|||||||
Reference in New Issue
Block a user