Commit 87d8adb1 authored by mae's avatar mae

Support camel-case for word-wise movement

This was DevDay's 3rd most requested feature

Done-with: Erik Verbruggen
parent d59c9173
......@@ -1077,6 +1077,382 @@ void BaseTextEditor::cleanWhitespace()
d->m_document->cleanWhitespace(textCursor());
}
// could go into QTextCursor...
static QTextLine currentTextLine(const QTextCursor &cursor)
{
const QTextBlock block = cursor.block();
if (!block.isValid())
return QTextLine();
const QTextLayout *layout = block.layout();
if (!layout)
return QTextLine();
const int relativePos = cursor.position() - block.position();
return layout->lineForTextPosition(relativePos);
}
bool BaseTextEditor::camelCaseLeft(QTextCursor &cursor, QTextCursor::MoveMode mode)
{
int state = 0;
enum Input {
Input_U,
Input_l,
Input_underscore,
Input_space,
Input_other
};
if (!cursor.movePosition(QTextCursor::Left, mode))
return false;
forever {
QChar c = characterAt(cursor.position());
Input input = Input_other;
if (c.isUpper())
input = Input_U;
else if (c.isLower() || c.isDigit())
input = Input_l;
else if (c == QLatin1Char('_'))
input = Input_underscore;
else if (c.isSpace() && c != QChar::ParagraphSeparator)
input = Input_space;
else
input = Input_other;
switch (state) {
case 0:
switch (input) {
case Input_U:
state = 1;
break;
case Input_l:
state = 2;
break;
case Input_underscore:
state = 3;
break;
case Input_space:
state = 4;
break;
default:
cursor.movePosition(QTextCursor::Right, mode);
return cursor.movePosition(QTextCursor::WordLeft, mode);
}
break;
case 1:
switch (input) {
case Input_U:
break;
default:
return true;
}
break;
case 2:
switch (input) {
case Input_U:
return true;
case Input_l:
break;
default:
cursor.movePosition(QTextCursor::Right, mode);
return true;
}
break;
case 3:
switch (input) {
case Input_underscore:
break;
case Input_U:
state = 1;
break;
case Input_l:
state = 2;
break;
default:
cursor.movePosition(QTextCursor::Right, mode);
return true;
}
break;
case 4:
switch (input) {
case Input_space:
break;
case Input_U:
state = 1;
break;
case Input_l:
state = 2;
break;
case Input_underscore:
state = 3;
break;
default:
cursor.movePosition(QTextCursor::Right, mode);
if (cursor.positionInBlock() == 0)
return true;
return cursor.movePosition(QTextCursor::WordLeft, mode);
}
}
if (!cursor.movePosition(QTextCursor::Left, mode))
return true;
}
}
bool BaseTextEditor::camelCaseRight(QTextCursor &cursor, QTextCursor::MoveMode mode)
{
int state = 0;
enum Input {
Input_U,
Input_l,
Input_underscore,
Input_space,
Input_other
};
forever {
QChar c = characterAt(cursor.position());
Input input = Input_other;
if (c.isUpper())
input = Input_U;
else if (c.isLower() || c.isDigit())
input = Input_l;
else if (c == QLatin1Char('_'))
input = Input_underscore;
else if (c.isSpace() && c != QChar::ParagraphSeparator)
input = Input_space;
else
input = Input_other;
switch (state) {
case 0:
switch (input) {
case Input_U:
state = 4;
break;
case Input_l:
state = 1;
break;
case Input_underscore:
state = 6;
break;
default:
return cursor.movePosition(QTextCursor::WordRight, mode);
}
break;
case 1:
switch (input) {
case Input_U:
return true;
case Input_l:
break;
case Input_underscore:
state = 6;
break;
case Input_space:
state = 7;
break;
default:
return true;
}
break;
case 2:
switch (input) {
case Input_U:
break;
case Input_l:
cursor.movePosition(QTextCursor::Left, mode);
return true;
case Input_space:
state = 7;
break;
default:
return cursor.movePosition(QTextCursor::WordRight, mode);
}
break;
case 4:
switch (input) {
case Input_U:
state = 2;
break;
case Input_l:
state = 1;
break;
case Input_underscore:
state = 6;
break;
case Input_space:
state = 7;
break;
default:
return cursor.movePosition(QTextCursor::WordRight, mode);
}
break;
case 6:
switch (input) {
case Input_underscore:
break;
case Input_space:
state = 7;
break;
default:
return cursor.movePosition(QTextCursor::WordRight, mode);
}
break;
case 7:
switch (input) {
case Input_space:
break;
default:
return true;
}
break;
}
cursor.movePosition(QTextCursor::Right, mode);
}
}
bool BaseTextEditor::cursorMoveKeyEvent(QKeyEvent *e)
{
QTextCursor cursor = textCursor();
QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
QTextCursor::MoveOperation op = QTextCursor::NoMove;
if (e == QKeySequence::MoveToNextChar) {
op = QTextCursor::Right;
}
else if (e == QKeySequence::MoveToPreviousChar) {
op = QTextCursor::Left;
}
else if (e == QKeySequence::SelectNextChar) {
op = QTextCursor::Right;
mode = QTextCursor::KeepAnchor;
}
else if (e == QKeySequence::SelectPreviousChar) {
op = QTextCursor::Left;
mode = QTextCursor::KeepAnchor;
}
else if (e == QKeySequence::SelectNextWord) {
op = QTextCursor::WordRight;
mode = QTextCursor::KeepAnchor;
}
else if (e == QKeySequence::SelectPreviousWord) {
op = QTextCursor::WordLeft;
mode = QTextCursor::KeepAnchor;
}
else if (e == QKeySequence::SelectStartOfLine) {
op = QTextCursor::StartOfLine;
mode = QTextCursor::KeepAnchor;
}
else if (e == QKeySequence::SelectEndOfLine) {
op = QTextCursor::EndOfLine;
mode = QTextCursor::KeepAnchor;
}
else if (e == QKeySequence::SelectStartOfBlock) {
op = QTextCursor::StartOfBlock;
mode = QTextCursor::KeepAnchor;
}
else if (e == QKeySequence::SelectEndOfBlock) {
op = QTextCursor::EndOfBlock;
mode = QTextCursor::KeepAnchor;
}
else if (e == QKeySequence::SelectStartOfDocument) {
op = QTextCursor::Start;
mode = QTextCursor::KeepAnchor;
}
else if (e == QKeySequence::SelectEndOfDocument) {
op = QTextCursor::End;
mode = QTextCursor::KeepAnchor;
}
else if (e == QKeySequence::SelectPreviousLine) {
op = QTextCursor::Up;
mode = QTextCursor::KeepAnchor;
}
else if (e == QKeySequence::SelectNextLine) {
op = QTextCursor::Down;
mode = QTextCursor::KeepAnchor;
{
QTextBlock block = cursor.block();
QTextLine line = currentTextLine(cursor);
if (!block.next().isValid()
&& line.isValid()
&& line.lineNumber() == block.layout()->lineCount() - 1)
op = QTextCursor::End;
}
}
else if (e == QKeySequence::MoveToNextWord) {
op = QTextCursor::WordRight;
}
else if (e == QKeySequence::MoveToPreviousWord) {
op = QTextCursor::WordLeft;
}
else if (e == QKeySequence::MoveToEndOfBlock) {
op = QTextCursor::EndOfBlock;
}
else if (e == QKeySequence::MoveToStartOfBlock) {
op = QTextCursor::StartOfBlock;
}
else if (e == QKeySequence::MoveToNextLine) {
op = QTextCursor::Down;
}
else if (e == QKeySequence::MoveToPreviousLine) {
op = QTextCursor::Up;
}
else if (e == QKeySequence::MoveToPreviousLine) {
op = QTextCursor::Up;
}
else if (e == QKeySequence::MoveToStartOfLine) {
op = QTextCursor::StartOfLine;
}
else if (e == QKeySequence::MoveToEndOfLine) {
op = QTextCursor::EndOfLine;
}
else if (e == QKeySequence::MoveToStartOfDocument) {
op = QTextCursor::Start;
}
else if (e == QKeySequence::MoveToEndOfDocument) {
op = QTextCursor::End;
}
else {
return false;
}
// Except for pageup and pagedown, Mac OS X has very different behavior, we don't do it all, but
// here's the breakdown:
// Shift still works as an anchor, but only one of the other keys can be down Ctrl (Command),
// Alt (Option), or Meta (Control).
// Command/Control + Left/Right -- Move to left or right of the line
// + Up/Down -- Move to top bottom of the file. (Control doesn't move the cursor)
// Option + Left/Right -- Move one word Left/right.
// + Up/Down -- Begin/End of Paragraph.
// Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
bool visualNavigation = cursor.visualNavigation();
cursor.setVisualNavigation(true);
bool moved = false;
if (op == QTextCursor::WordRight) {
moved = camelCaseRight(cursor, mode);
} else if (op == QTextCursor::WordLeft) {
moved = camelCaseLeft(cursor, mode);
} else {
moved = cursor.movePosition(op, mode);
}
cursor.setVisualNavigation(visualNavigation);
if (moved) {
setTextCursor(cursor);
ensureCursorVisible();
}
return true;
}
void BaseTextEditor::keyPressEvent(QKeyEvent *e)
{
viewport()->setCursor(Qt::BlankCursor);
......@@ -1335,7 +1711,10 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
}
if (ro || e->text().isEmpty() || !e->text().at(0).isPrint()) {
QPlainTextEdit::keyPressEvent(e);
if (cursorMoveKeyEvent(e))
;
else
QPlainTextEdit::keyPressEvent(e);
} else if ((e->modifiers() & (Qt::ControlModifier|Qt::AltModifier)) != Qt::ControlModifier){
QTextCursor cursor = textCursor();
QString text = e->text();
......
......@@ -525,6 +525,11 @@ private:
void universalHelper(); // test function for development
bool cursorMoveKeyEvent(QKeyEvent *e);
bool camelCaseRight(QTextCursor &cursor, QTextCursor::MoveMode mode);
bool camelCaseLeft(QTextCursor &cursor, QTextCursor::MoveMode mode);
private slots:
// auto completion
void _q_requestAutoCompletion();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment