diff --git a/share/qtcreator/snippets/qml.xml b/share/qtcreator/snippets/qml.xml new file mode 100644 index 00000000000..4a2c3a5f2ea --- /dev/null +++ b/share/qtcreator/snippets/qml.xml @@ -0,0 +1,96 @@ + + +property name name : name + +Item { + id: name + + } + +BorderImage { + id: name + width: name; height: name + border.left: name; border.top: name + border.right: name; border.bottom: name + source: "name" +} + + +Image { + id: name + source: "name" +} + + +Text { + id: name + text: "name" +} + + +states: [ + State { + name: "name" + PropertyChanges { + target: name + + } + } +] + + +State { + name: "name" + PropertyChanges { + target: name + + } +} + + +transitions: [ + Transition { + from: "name" + to: "name" + + } +] + + +Transition { + from: "name" + to: "name" + +} + + +PropertyChanges { + target: name + + } + + +NumberAnimation { matchTargets: "name"; matchProperties: "name"; duration: 200 } + +NumberAnimation { target: "name"; property: "name"; value: name; duration: 200 } + +PropertyAction { matchTargets: "name"; matchProperties: "name"; duration: 200 } + +PropertyAction { target: "name"; property: "name"; value: name; duration: 200 } + +PauseAnimation { duration: name} + +ColorAnimation { from: name; to: name; duration: 200 } + +effect: Colorize { color: "name" } + +effect: Blur { blurRadius: "200" } + +effect: DropShadow { + blurRadius: 200 + offset.x: 200 + offset.y: 200 + } + + + diff --git a/src/plugins/qmljseditor/qmlcodecompletion.cpp b/src/plugins/qmljseditor/qmlcodecompletion.cpp index af7391854ba..56376c13c0e 100644 --- a/src/plugins/qmljseditor/qmlcodecompletion.cpp +++ b/src/plugins/qmljseditor/qmlcodecompletion.cpp @@ -37,6 +37,12 @@ #include #include +#include + +#include +#include +#include + #include using namespace QmlJSEditor; @@ -134,61 +140,9 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) } } + updateSnippets(); - // snippets completion - TextEditor::CompletionItem item(this); - item.text = QLatin1String("Rectangle - declaration"); - item.data = QVariant::fromValue(QString("Rectangle {\nwidth: $100$;\nheight: 100;\n$$\n}")); - m_completions.append(item); - - item.text = QLatin1String("Item - declaration"); - item.data = QVariant::fromValue(QString("Item {\nwidth: $100$;\nheight: 100;\n$$\n}")); - m_completions.append(item); - - item.text = QLatin1String("BorderImage - declaration"); - item.data = QLatin1String("BorderImage {\n" - "id: $name$;\n" - "width: $100$; height: $100$;\n" - "border.left: $2$; border.top: $2$;\n" - "border.right: $2$; border.bottom: $2$;\n" - "source: \"$name$\";\n" - "}\n"); - m_completions.append(item); - - item.text = QLatin1String("State - declaration"); - item.data = QLatin1String("State {\n" - "name: \"$state$;\"\n" - "PropertyChanges {\n" - "target: $target$;\n" - "$$\n" - "}\n" - "}\n"); - m_completions.append(item); - - item.text = QLatin1String("states - declaration"); - item.data = QLatin1String("states: [\n" - "State {\n" - "name: \"$state$\";\n" - "PropertyChanges {\n" - "target: $target$;\n" - "$$\n" - "}\n" - "}\n" - "]\n"); - m_completions.append(item); - - item.text = QLatin1String("property - declaration"); - item.data = QLatin1String("property $var$ $name$: $value$;\n"); - m_completions.append(item); - - item.text = QLatin1String("readonly property - declaration"); - item.data = QLatin1String("readonly property $var$ $name$: $value$;\n"); - m_completions.append(item); - - item.text = QLatin1String("NumericAnimation - declaration"); - item.data = QLatin1String("NumberAnimation { matchTargets: \"$target$\"; matchProperties: \"$properties$\"; duration: $1000$ }\n"); - m_completions.append(item); - + m_completions.append(m_snippets); return pos; } @@ -299,3 +253,65 @@ void QmlCodeCompletion::cleanup() m_completions.clear(); } + +void QmlCodeCompletion::updateSnippets() +{ + QString qmlsnippets = Core::ICore::instance()->resourcePath() + QLatin1String("/snippets/qml.xml"); + if (!QFile::exists(qmlsnippets)) + return; + + QDateTime lastModified = QFileInfo(qmlsnippets).lastModified(); + if (!m_snippetFileLastModified.isNull() && lastModified == m_snippetFileLastModified) + return; + + m_snippetFileLastModified = lastModified; + QFile file(qmlsnippets); + file.open(QIODevice::ReadOnly); + QXmlStreamReader xml(&file); + while (!xml.atEnd() && xml.readNextStartElement()) { + if (xml.name() == QLatin1String("snippets")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("snippet")) { + TextEditor::CompletionItem item(this); + QString title, data; + QString description = xml.attributes().value("description").toString(); + + while (!xml.atEnd()) { + xml.readNext(); + if (xml.isEndElement()) { + int i = 0; + while (i < data.size() && data.at(i).isLetterOrNumber()) + ++i; + title = data.left(i); + item.text = title; + if (!description.isEmpty()) { + item.text += QLatin1Char(' '); + item.text += description; + } + item.data = QVariant::fromValue(data); + m_snippets.append(item); + break; + + } + + if (xml.isCharacters()) + data += xml.text(); + else if (xml.isStartElement()) { + if (xml.name() != QLatin1String("tab")) + xml.raiseError(QLatin1String("invalid snippets file")); + else { + data += QChar::ObjectReplacementCharacter; + data += xml.readElementText(); + data += QChar::ObjectReplacementCharacter; + } + } + + } + } + } + } + } + if (xml.hasError()) + qWarning() << qmlsnippets << xml.errorString(); + file.close(); +} diff --git a/src/plugins/qmljseditor/qmlcodecompletion.h b/src/plugins/qmljseditor/qmlcodecompletion.h index cb8687cde66..8adc066fc9f 100644 --- a/src/plugins/qmljseditor/qmlcodecompletion.h +++ b/src/plugins/qmljseditor/qmlcodecompletion.h @@ -32,6 +32,7 @@ #include #include +#include namespace TextEditor { class ITextEditable; @@ -69,6 +70,10 @@ private: QList m_completions; Qt::CaseSensitivity m_caseSensitivity; QmlJS::TypeSystem *m_typeSystem; + + QList m_snippets; + QDateTime m_snippetFileLastModified; + void updateSnippets(); }; diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp index 7fc9eaf18fa..718e757803d 100644 --- a/src/plugins/texteditor/basetexteditor.cpp +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -960,11 +960,15 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e) d->m_lastEventWasBlockSelectionEvent = false; if (e->key() == Qt::Key_Escape) { - e->accept(); - QTextCursor cursor = textCursor(); - cursor.clearSelection(); - setTextCursor(cursor); - return; + if (d->m_snippetOverlay->isVisible()) { + e->accept(); + d->m_snippetOverlay->hide(); + d->m_snippetOverlay->clear(); + QTextCursor cursor = textCursor(); + cursor.clearSelection(); + setTextCursor(cursor); + return; + } } bool ro = isReadOnly(); @@ -996,6 +1000,17 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e) || (!d->m_lineSeparatorsAllowed && e == QKeySequence::InsertLineSeparator)) ) { + if (d->m_snippetOverlay->isVisible()) { + e->accept(); + d->m_snippetOverlay->hide(); + d->m_snippetOverlay->clear(); + QTextCursor cursor = textCursor(); + cursor.movePosition(QTextCursor::EndOfBlock); + setTextCursor(cursor); + return; + } + + QTextCursor cursor = textCursor(); if (d->m_inBlockSelectionMode) cursor.clearSelection(); @@ -1190,9 +1205,10 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e) return; } - if (d->m_snippetOverlay->isVisible() && - (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)) + if (d->m_snippetOverlay->isVisible() + && (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)) { d->snippetCheckCursor(textCursor()); + } if (ro || e->text().isEmpty() || !e->text().at(0).isPrint()) { QPlainTextEdit::keyPressEvent(e); @@ -1263,21 +1279,21 @@ void BaseTextEditor::insertCodeSnippet(const QString &snippet) QMap positions; while (pos < snippet.size()) { - if (snippet.at(pos) != QLatin1Char('$')) { + if (snippet.at(pos) != QChar::ObjectReplacementCharacter) { const int start = pos; do { ++pos; } - while (pos < snippet.size() && snippet.at(pos) != QLatin1Char('$')); + while (pos < snippet.size() && snippet.at(pos) != QChar::ObjectReplacementCharacter); cursor.insertText(snippet.mid(start, pos - start)); } else { // the start of a place holder. const int start = ++pos; for (; pos < snippet.size(); ++pos) { - if (snippet.at(pos) == QLatin1Char('$')) + if (snippet.at(pos) == QChar::ObjectReplacementCharacter) break; } Q_ASSERT(pos < snippet.size()); - Q_ASSERT(snippet.at(pos) == QLatin1Char('$')); + Q_ASSERT(snippet.at(pos) == QChar::ObjectReplacementCharacter); const QString textToInsert = snippet.mid(start, pos - start); @@ -1319,7 +1335,12 @@ void BaseTextEditor::insertCodeSnippet(const QString &snippet) const QTextEdit::ExtraSelection &selection = selections.first(); cursor = textCursor(); - cursor.setPosition(selection.cursor.anchor() + 1); + if (selection.cursor.hasSelection()) { + cursor.setPosition(selection.cursor.selectionStart()+1); + cursor.setPosition(selection.cursor.selectionEnd(), QTextCursor::KeepAnchor); + } else { + cursor.setPosition(selection.cursor.position()); + } setTextCursor(cursor); } } @@ -1416,8 +1437,13 @@ bool BaseTextEditor::event(QEvent *e) d->m_contentsChanged = false; switch (e->type()) { case QEvent::ShortcutOverride: + if (static_cast(e)->key() == Qt::Key_Escape && d->m_snippetOverlay->isVisible()) { + e->accept(); + return true; + } e->ignore(); // we are a really nice citizen return true; + break; default: break; } @@ -1746,7 +1772,8 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward) if (forward) { for (int i = 0; i < m_snippetOverlay->m_selections.count(); ++i){ const OverlaySelection &selection = m_snippetOverlay->m_selections.at(i); - if (selection.m_cursor_begin.position() > cursor.position()) { + if (selection.m_cursor_begin.position() >= cursor.position() + && selection.m_cursor_end.position() > cursor.position()) { final = selection; break; } diff --git a/src/plugins/texteditor/texteditoroverlay.h b/src/plugins/texteditor/texteditoroverlay.h index 5e8c6d76874..9f98c38c9ac 100644 --- a/src/plugins/texteditor/texteditoroverlay.h +++ b/src/plugins/texteditor/texteditoroverlay.h @@ -72,6 +72,9 @@ public: bool isVisible() const { return m_visible; } void setVisible(bool b); + inline void hide() { setVisible(false); } + inline void show() { setVisible(true); } + void setBorderWidth(int bw) {m_borderWidth = bw; } void update();