diff options
author | David Schulz <david.schulz@qt.io> | 2021-11-05 08:56:49 +0100 |
---|---|---|
committer | David Schulz <david.schulz@qt.io> | 2021-11-08 08:35:38 +0000 |
commit | e0c115fb9b1806e77930bbc9d4484eaefa8aee5f (patch) | |
tree | ae35b84620eac0e3033df8ddb486b2d1276e9400 | |
parent | af7d9d6f8e6988e0fc4441841c9d1c8931bdc627 (diff) |
Editor: Allow creating block selections with alt+shift again
These block selections are a convenience for people used to the block
selection allowing to also unselect parts again (at least as long as
alt+shift is pressed).
Fixes: QTCREATORBUG-26535
Fixes: QTCREATORBUG-26529
Change-Id: I19558dc1d823c268cc1cfda0ea8151bac483701f
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
-rw-r--r-- | src/libs/utils/multitextcursor.cpp | 2 | ||||
-rw-r--r-- | src/libs/utils/multitextcursor.h | 3 | ||||
-rw-r--r-- | src/plugins/texteditor/tabsettings.cpp | 5 | ||||
-rw-r--r-- | src/plugins/texteditor/tabsettings.h | 1 | ||||
-rw-r--r-- | src/plugins/texteditor/texteditor.cpp | 109 |
5 files changed, 117 insertions, 3 deletions
diff --git a/src/libs/utils/multitextcursor.cpp b/src/libs/utils/multitextcursor.cpp index 7225de9344..a73c58bbb1 100644 --- a/src/libs/utils/multitextcursor.cpp +++ b/src/libs/utils/multitextcursor.cpp @@ -294,7 +294,7 @@ static QTextLine currentTextLine(const QTextCursor &cursor) return layout->lineForTextPosition(relativePos); } -bool multiCursorAddEvent(QKeyEvent *e, QKeySequence::StandardKey matchKey) +bool MultiTextCursor::multiCursorAddEvent(QKeyEvent *e, QKeySequence::StandardKey matchKey) { uint searchkey = (e->modifiers() | e->key()) & ~(Qt::KeypadModifier diff --git a/src/libs/utils/multitextcursor.h b/src/libs/utils/multitextcursor.h index 90938848e8..8fdf041faa 100644 --- a/src/libs/utils/multitextcursor.h +++ b/src/libs/utils/multitextcursor.h @@ -27,6 +27,7 @@ #include "utils_global.h" +#include <QKeySequence> #include <QTextCursor> QT_BEGIN_NAMESPACE @@ -99,6 +100,8 @@ public: const_iterator constBegin() const { return m_cursors.constBegin(); } const_iterator constEnd() const { return m_cursors.constEnd(); } + static bool multiCursorAddEvent(QKeyEvent *e, QKeySequence::StandardKey matchKey); + private: QList<QTextCursor> m_cursors; }; diff --git a/src/plugins/texteditor/tabsettings.cpp b/src/plugins/texteditor/tabsettings.cpp index c61a0b3fb3..c363815572 100644 --- a/src/plugins/texteditor/tabsettings.cpp +++ b/src/plugins/texteditor/tabsettings.cpp @@ -204,6 +204,11 @@ int TabSettings::columnAt(const QString &text, int position) const return column; } +int TabSettings::columnAtCursorPosition(const QTextCursor &cursor) const +{ + return columnAt(cursor.block().text(), cursor.positionInBlock()); +} + int TabSettings::positionAtColumn(const QString &text, int column, int *offset, bool allowOverstep) const { int col = 0; diff --git a/src/plugins/texteditor/tabsettings.h b/src/plugins/texteditor/tabsettings.h index e73d81b365..c6446046a8 100644 --- a/src/plugins/texteditor/tabsettings.h +++ b/src/plugins/texteditor/tabsettings.h @@ -66,6 +66,7 @@ public: int lineIndentPosition(const QString &text) const; int columnAt(const QString &text, int position) const; + int columnAtCursorPosition(const QTextCursor &cursor) const; int positionAtColumn(const QString &text, int column, int *offset = nullptr, bool allowOverstep = false) const; int columnCountForText(const QString &text, int startColumn = 0) const; int indentedColumn(int column, bool doIndent = true) const; diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 7a76970389..499897522d 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -782,6 +782,18 @@ public: bool m_scrollBarUpdateScheduled = false; const MultiTextCursor m_cursors; + struct BlockSelection + { + int blockNumber = -1; + int column = -1; + int anchorBlockNumber = -1; + int anchorColumn = -1; + }; + QList<BlockSelection> m_blockSelections; + QList<QTextCursor> generateCursorsForBlockSelection(const BlockSelection &blockSelection); + void initBlockSelection(); + void clearBlockSelection(); + void handleMoveBlockSelection(QTextCursor::MoveOperation op); class UndoCursor { @@ -1348,6 +1360,81 @@ void TextEditorWidgetPrivate::updateAutoCompleteHighlight() q->setExtraSelections(TextEditorWidget::AutoCompleteSelection, extraSelections); } +QList<QTextCursor> TextEditorWidgetPrivate::generateCursorsForBlockSelection( + const BlockSelection &blockSelection) +{ + const TabSettings tabSettings = m_document->tabSettings(); + + QList<QTextCursor> result; + QTextBlock block = m_document->document()->findBlockByNumber(blockSelection.anchorBlockNumber); + QTextCursor cursor(block); + cursor.setPosition(block.position() + + tabSettings.positionAtColumn(block.text(), blockSelection.anchorColumn)); + + const bool forward = blockSelection.blockNumber > blockSelection.anchorBlockNumber + || (blockSelection.blockNumber == blockSelection.anchorBlockNumber + && blockSelection.column == blockSelection.anchorColumn); + + while (block.isValid()) { + const QString &blockText = block.text(); + cursor.setPosition(block.position() + + tabSettings.positionAtColumn(blockText, blockSelection.anchorColumn)); + cursor.setPosition(block.position() + + tabSettings.positionAtColumn(blockText, blockSelection.column), + QTextCursor::KeepAnchor); + result.append(cursor); + if (block.blockNumber() == blockSelection.blockNumber) + break; + block = forward ? block.next() : block.previous(); + } + return result; +} + +void TextEditorWidgetPrivate::initBlockSelection() +{ + const TabSettings tabSettings = m_document->tabSettings(); + for (const QTextCursor &cursor : m_cursors) { + const int column = tabSettings.columnAtCursorPosition(cursor); + QTextCursor anchor = cursor; + anchor.setPosition(anchor.anchor()); + const int anchorColumn = tabSettings.columnAtCursorPosition(anchor); + m_blockSelections.append({cursor.blockNumber(), column, anchor.blockNumber(), anchorColumn}); + } +} + +void TextEditorWidgetPrivate::clearBlockSelection() +{ + m_blockSelections.clear(); +} + +void TextEditorWidgetPrivate::handleMoveBlockSelection(QTextCursor::MoveOperation op) +{ + if (m_blockSelections.isEmpty()) + initBlockSelection(); + QList<QTextCursor> cursors; + for (BlockSelection &blockSelection : m_blockSelections) { + switch (op) { + case QTextCursor::Up: + blockSelection.blockNumber = qMax(0, blockSelection.blockNumber - 1); + break; + case QTextCursor::Down: + blockSelection.blockNumber = qMin(m_document->document()->blockCount() - 1, + blockSelection.blockNumber + 1); + break; + case QTextCursor::NextCharacter: + ++blockSelection.column; + break; + case QTextCursor::PreviousCharacter: + blockSelection.column = qMax(0, blockSelection.column - 1); + break; + default: + return; + } + cursors.append(generateCursorsForBlockSelection(blockSelection)); + } + q->setMultiTextCursor(MultiTextCursor(cursors)); +} + void TextEditorWidget::selectEncoding() { TextDocument *doc = d->m_document.data(); @@ -2181,6 +2268,8 @@ static inline bool isPrintableText(const QString &text) void TextEditorWidget::keyPressEvent(QKeyEvent *e) { + ExecuteOnDestruction eod([&]() { d->clearBlockSelection(); }); + if (!isModifier(e) && mouseHidingEnabled()) viewport()->setCursor(Qt::BlankCursor); ToolTip::hide(); @@ -2420,7 +2509,23 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e) } if (ro || !isPrintableText(eventText)) { - if (!d->cursorMoveKeyEvent(e)) { + QTextCursor::MoveOperation blockSelectionOperation = QTextCursor::NoMove; + if (e->modifiers() & Qt::AltModifier && !Utils::HostOsInfo::isMacHost()) { + if (MultiTextCursor::multiCursorAddEvent(e, QKeySequence::MoveToNextLine)) + blockSelectionOperation = QTextCursor::Down; + else if (MultiTextCursor::multiCursorAddEvent(e, QKeySequence::MoveToPreviousLine)) + blockSelectionOperation = QTextCursor::Up; + else if (MultiTextCursor::multiCursorAddEvent(e, QKeySequence::MoveToNextChar)) + blockSelectionOperation = QTextCursor::NextCharacter; + else if (MultiTextCursor::multiCursorAddEvent(e, QKeySequence::MoveToPreviousChar)) + blockSelectionOperation = QTextCursor::PreviousCharacter; + } + + if (blockSelectionOperation != QTextCursor::NoMove) { + auto doNothing = [](){}; + eod.reset(doNothing); + d->handleMoveBlockSelection(blockSelectionOperation); + } else if (!d->cursorMoveKeyEvent(e)) { QTextCursor cursor = textCursor(); bool cursorWithinSnippet = false; if (d->m_snippetOverlay->isVisible() @@ -5134,7 +5239,7 @@ void TextEditorWidget::mouseMoveEvent(QMouseEvent *e) cursor.addCursor(c); } cursor.mergeCursors(); - if (!cursor.isNull() && cursor != multiTextCursor()) + if (!cursor.isNull()) setMultiTextCursor(cursor); } else { if (startMouseMoveCursor.has_value()) |