diff --git a/pglab/CodeEditor.cpp b/pglab/CodeEditor.cpp index 5495ff6..79c8488 100644 --- a/pglab/CodeEditor.cpp +++ b/pglab/CodeEditor.cpp @@ -2,6 +2,7 @@ #include "EditorGutter.h" #include #include +#include // // Adapted from codeeditor example // http://doc.qt.io/qt-5/qtwidgets-widgets-codeeditor-example.html @@ -22,6 +23,7 @@ CodeEditor::CodeEditor(QWidget *parent) setWordWrapMode(QTextOption::NoWrap); updateGutterAreaWidth(0); + setTabSize(4); highlightCurrentLine(); } @@ -34,7 +36,7 @@ int CodeEditor::gutterAreaWidth() ++digits; } - int space = 3 + fontMetrics().width(QLatin1Char('9')) * (digits + 2); + int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * (digits + 2); return space; } @@ -154,3 +156,118 @@ void CodeEditor::gutterAreaPaintEvent(QPaintEvent *event) ++blockNumber; } } + +void CodeEditor::keyPressEvent(QKeyEvent *e) +{ + auto k = e->key(); + qDebug() << QString("%1").arg(k, 0, 16); + if (k == Qt::Key_Tab) { + // Function returns false if there was no selection to indent + if (indentSelection(true)) + return; + } + else if (k == Qt::Key_Backtab) { + // Function returns false if there was no selection to indent + if (indentSelection(false)) + return; + } + else if (k == Qt::Key_Equal || k == Qt::Key_Plus) { + if (e->modifiers().testFlag(Qt::ControlModifier)) { + auto f = font(); + f.setPointSize(f.pointSize() + 1); + setFont(f); + } + } + else if (k == Qt::Key_Minus) { + if (e->modifiers().testFlag(Qt::ControlModifier)) { + auto f = font(); + f.setPointSize(f.pointSize() - 1); + setFont(f); + } + } + QPlainTextEdit::keyPressEvent(e); +} + +bool CodeEditor::indentSelection(bool indent) +{ + auto cursor = textCursor(); + + if(!cursor.hasSelection()) + return false; + + auto first_pos = cursor.anchor(); + auto end_pos = cursor.position(); + + if(first_pos > end_pos) + std::swap(first_pos, end_pos); + + cursor.setPosition(first_pos, QTextCursor::MoveAnchor); + auto start_block = cursor.block().blockNumber(); + + cursor.setPosition(end_pos, QTextCursor::MoveAnchor); + auto end_block = cursor.block().blockNumber(); + if (end_block == start_block) + return false; + + cursor.beginEditBlock(); + cursor.setPosition(first_pos, QTextCursor::MoveAnchor); + const auto block_count = end_block - start_block; + for(int block = 0; block <= block_count; ++block) { + cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); + if (indent) { + if (m_useTab) + cursor.insertText("\t"); + else + cursor.insertText(QString(m_tabSize, ' ')); + } + else { + // remove tab if there is a tab + cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); + const QString text = cursor.selectedText(); + int index = 0; + int pos = 0; + while (pos < m_tabSize && index < text.length()) { + QChar c = text[index++]; + if (c == ' ') + ++pos; + else if (c == '\t') + pos = ((pos + m_tabSize) / m_tabSize) * m_tabSize; + else { + --index; + break; + } + } + cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); + for (int i = 0; i < index; ++i) + cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + } + cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor); + } + cursor.endEditBlock(); + + cursor.setPosition(first_pos, QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); + while(cursor.block().blockNumber() < end_block) + cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor); + + cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + + setTextCursor(cursor); + return true; +} + +void CodeEditor::setFont(const QFont &f) +{ + QWidget::setFont(f); + auto orig_tab = m_tabSize; + m_tabSize = 0; + setTabSize(orig_tab); +} + +void CodeEditor::setTabSize(int chars) +{ + m_tabSize = chars; + int pixels = fontMetrics().horizontalAdvance(QString(chars, '0')); + this->setTabStopDistance(pixels); +} diff --git a/pglab/CodeEditor.h b/pglab/CodeEditor.h index ec6910a..6bfc64b 100644 --- a/pglab/CodeEditor.h +++ b/pglab/CodeEditor.h @@ -18,8 +18,11 @@ public: void addErrorMarker(int position, int length); void clearErrorMarkers(); + void setFont(const QFont &); + void setTabSize(int chars); protected: void resizeEvent(QResizeEvent *event) override; + void keyPressEvent(QKeyEvent *e) override; signals: @@ -31,6 +34,7 @@ private slots: void updateGutterArea(const QRect &, int); void onTextChanged(); + private: QWidget *gutterArea; @@ -39,7 +43,13 @@ private: std::set errorLines; + // Settings + int m_tabSize = 0; // tabSize in characters + bool m_useTab = false; + void updateExtraSelections(); + bool indentSelection(bool indent); + }; #endif // CODEEDITOR_H