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>
This commit is contained in:
David Schulz
2021-11-05 08:56:49 +01:00
parent af7d9d6f8e
commit e0c115fb9b
5 changed files with 117 additions and 3 deletions

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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())