QCodeEdit blockselection almost works, without copypaste and with bad visual

This commit is contained in:
2020-09-22 20:45:23 +03:00
parent ca020b0319
commit 743b7c8e28
2 changed files with 309 additions and 137 deletions

View File

@@ -49,7 +49,7 @@ QCodeEdit::QCodeEdit(QWidget * parent): QWidget(parent) {
overlay = 0;
prev_lc = auto_comp_pl = cur_search_ind = pos_press = pos_el_press = -1;
timer_parse = 0;
_ignore_focus_out = _destructor = _replacing = cursor_state = false;
_ignore_focus_out = _destructor = _replacing = cursor_state = block_sel_state = false;
_first = true;
qRegisterMetaType<QTextCursor>();
qRegisterMetaType<QCodeEdit::ACEntry>();
@@ -454,21 +454,20 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) {
if (ui->textCode) {
if (o == ui->textCode->viewport()) {
if (e->type() == QEvent::MouseButtonPress) {
cancelBlockSelection();
completer->hide();
hideHelp();
}
if (e->type() == QEvent::MouseMove && completer->isHidden()) {
QMouseEvent * me = (QMouseEvent*)e;
if (me->modifiers().testFlag(Qt::ControlModifier)) {
showLink();
}
}
if (e->type() == QEvent::MouseButtonPress) {
QMouseEvent * me = (QMouseEvent*)e;
if (me->modifiers().testFlag(Qt::ControlModifier) && (me->button() == Qt::LeftButton)) {
gotoLink();
}
}
if (e->type() == QEvent::MouseMove && completer->isHidden()) {
QMouseEvent * me = (QMouseEvent*)e;
switchBlockSelection();
if (me->modifiers().testFlag(Qt::ControlModifier))
showLink();
}
if (e->type() == QEvent::Paint) {
resizeOverlay();
}
@@ -477,8 +476,6 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) {
if (o == ui->textCode) {
//qDebug() << e;
QMetaObject::invokeMethod(this, "syncScrolls", Qt::QueuedConnection);
QKeyEvent * ke;
QChar kc(0);
switch (e->type()) {
case QEvent::ToolTip:
if (completer->isHidden()) {
@@ -488,112 +485,9 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) {
}
break;
case QEvent::KeyPress:
ke = (QKeyEvent * )e;
//qDebug() << "key" << ke;
switch (ke->key()) {
case Qt::Key_Space:
if (ke->modifiers().testFlag(Qt::ControlModifier)) {
invokeAutoCompletition(true);
return true;
}
break;
case Qt::Key_Escape:
hideHelp();
if (completer->isVisible())
completer->hide();
else
hideSearch();
break;
case Qt::Key_Up:
if (completer->isVisible()) {
completer->previousCompletition();
return true;
}
completer->hide();
hideHelp();
if (ke->modifiers().testFlag(Qt::AltModifier)) {
copyLineUp();
return true;
}
if (ke->modifiers().testFlag(Qt::ControlModifier) && ke->modifiers().testFlag(Qt::ShiftModifier)) {
moveLineUp();
return true;
}
break;
case Qt::Key_Down:
if (completer->isVisible()) {
completer->nextCompletition();
return true;
}
completer->hide();
hideHelp();
if (ke->modifiers().testFlag(Qt::AltModifier)) {
copyLineDown();
return true;
}
if (ke->modifiers().testFlag(Qt::ControlModifier) && ke->modifiers().testFlag(Qt::ShiftModifier)) {
moveLineDown();
return true;
}
break;
case Qt::Key_Home:
case Qt::Key_End:
case Qt::Key_PageUp:
case Qt::Key_PageDown:
if (completer->isVisible()) {
qApp->sendEvent(completer, new QKeyEvent(e->type(), ke->key(), ke->modifiers()));
return true;
}
break;
case Qt::Key_Left:
case Qt::Key_Right:
case Qt::Key_Backspace:
case Qt::Key_Delete:
if (completer->isVisible())
QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false));
break;
case Qt::Key_Return:
if (completer->isVisible()) {
commitCompletition();
completer->hide();
return true;
}
if (ui->textCode->textCursor().selectedText().isEmpty())
QMetaObject::invokeMethod(this, "autoIndent", Qt::QueuedConnection);
break;
case Qt::Key_Tab:
if (!ui->textCode->textCursor().selectedText().isEmpty()) {
if (ke->modifiers().testFlag(Qt::ShiftModifier))
deindent();
else
indent();
return true;
}
break;
case Qt::Key_D:
if (ke->modifiers().testFlag(Qt::ControlModifier)) {
completer->hide();
return true;
}
break;
case Qt::Key_Control:
showLink();
break;
case Qt::Key_F1:
if (widget_help->isVisible())
gotoHelpHRef(help_entry);
break;
default: break;
}
if (!ke->text().isEmpty())
kc = ke->text()[0];
if (kc == '.') {
completer->hide();
QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false));
} else {
if ((kc.isLetterOrNumber() || kc.toLatin1() == '_') && completer->isVisible())
QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false));
}
if (codeKeyEvent((QKeyEvent * )e))
return true;
break;
case QEvent::KeyRelease:
hideLink();
@@ -661,6 +555,138 @@ void QCodeEdit::changeEvent(QEvent * e) {
}
bool QCodeEdit::codeKeyEvent(QKeyEvent * ke) {
QChar kc;
switch (ke->key()) {
case Qt::Key_Space:
if (ke->modifiers().testFlag(Qt::ControlModifier)) {
invokeAutoCompletition(true);
return true;
}
break;
case Qt::Key_Escape:
hideHelp();
if (completer->isVisible())
completer->hide();
else
hideSearch();
break;
case Qt::Key_Up:
switchBlockSelection(ke);
if (completer->isVisible()) {
completer->previousCompletition();
return true;
}
completer->hide();
hideHelp();
if (ke->modifiers().testFlag(Qt::AltModifier)) {
if (ke->modifiers().testFlag(Qt::ShiftModifier))
return false;
else
copyLineUp();
return true;
}
if (ke->modifiers().testFlag(Qt::ControlModifier) && ke->modifiers().testFlag(Qt::ShiftModifier)) {
moveLineUp();
return true;
}
break;
case Qt::Key_Down:
switchBlockSelection(ke);
if (completer->isVisible()) {
completer->nextCompletition();
return true;
}
completer->hide();
hideHelp();
if (ke->modifiers().testFlag(Qt::AltModifier)) {
if (ke->modifiers().testFlag(Qt::ShiftModifier))
return false;
else
copyLineDown();
return true;
}
if (ke->modifiers().testFlag(Qt::ControlModifier) && ke->modifiers().testFlag(Qt::ShiftModifier)) {
moveLineDown();
return true;
}
break;
case Qt::Key_Home:
case Qt::Key_End:
case Qt::Key_PageUp:
case Qt::Key_PageDown:
switchBlockSelection(ke);
if (completer->isVisible()) {
qApp->sendEvent(completer, new QKeyEvent(ke->type(), ke->key(), ke->modifiers()));
return true;
}
break;
case Qt::Key_Left:
case Qt::Key_Right:
switchBlockSelection(ke);
if (hasBlockSelection()) break;
case Qt::Key_Backspace:
case Qt::Key_Delete:
if (completer->isVisible())
QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false));
if (removeBlockSelection(ke->key() == Qt::Key_Delete))
return true;
break;
case Qt::Key_Return:
if (hasBlockSelection()) {
cancelBlockSelection();
break;
}
if (completer->isVisible()) {
commitCompletition();
completer->hide();
return true;
}
if (ui->textCode->textCursor().selectedText().isEmpty())
QMetaObject::invokeMethod(this, "autoIndent", Qt::QueuedConnection);
break;
case Qt::Key_Tab:
if (!ui->textCode->textCursor().selectedText().isEmpty()) {
if (ke->modifiers().testFlag(Qt::ShiftModifier))
deindent();
else
indent();
return true;
}
break;
case Qt::Key_D:
if (ke->modifiers().testFlag(Qt::ControlModifier)) {
completer->hide();
return true;
}
break;
case Qt::Key_Control:
showLink();
break;
case Qt::Key_F1:
if (widget_help->isVisible())
gotoHelpHRef(help_entry);
break;
default: break;
}
if (!ke->text().isEmpty()) {
if (hasBlockSelection() && (ke->modifiers() == 0 || ke->modifiers() == Qt::ShiftModifier)) {
insertBlockSelection(ke->text());
return true;
}
kc = ke->text()[0];
}
if (kc == '.') {
completer->hide();
QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false));
} else {
if ((kc.isLetterOrNumber() || kc.toLatin1() == '_') && completer->isVisible())
QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false));
}
return false;
}
char antiBracket(char c) {
switch (c) {
case '(': return ')';
@@ -747,34 +773,143 @@ int QCodeEdit::searchIndFromCursor() {
}
QRect QCodeEdit::cursorRect(QRect * line) {
QRect r = ui->textCode->cursorRect(textCursor()), lr = r;
if (hasBlockSelection()) {
r |= ui->textCode->cursorRect(block_start_cursor);
lr.setY(r.y());
lr.setHeight(r.height());
}
lr.setWidth(cursor_width);
if (line) *line = lr;
return (r | lr);
}
QRect QCodeEdit::blockSelectionRect() {
QTextCursor tc = ui->textCode->textCursor();
QPoint ps(block_start_cursor.columnNumber(), block_start_cursor.blockNumber()),
pe(tc.columnNumber(), tc.blockNumber());
QRect bsr(QPoint(qMin(ps.x(), pe.x()), qMin(ps.y(), pe.y())),
QSize(qAbs(ps.x() - pe.x()), qAbs(ps.y() - pe.y()) + 1));
return bsr;
}
void QCodeEdit::repaintCursor() {
QRect r = ui->textCode->cursorRect(ui->textCode->textCursor());
r.setWidth(cursor_width);
//r.translate(-cursor_width, 0);
overlay->update(r);
overlay->update(cursorRect());
}
void QCodeEdit::drawCursor() {
QPainter p(overlay);
QTextCursor tc = textCursor();
if (cursor_state && ui->textCode->hasFocus()) {
QTextLayout * lay = tc.block().layout();
if (lay) {
//QTextLine l = lay->lineForTextPosition(tc.positionInBlock());
//int x = l.cursorToX(tc.position(), QTextLine::Trailing);
p.setPen(Qt::black);
QRect r = ui->textCode->cursorRect(tc);
r.setWidth(cursor_width);
r.adjust(0, 1, 0, -1);
//r.translate(-cursor_width, 0);
p.setCompositionMode(QPainter::CompositionMode_Difference);
p.fillRect(r, Qt::white);
//p.fillRect(p.viewport(), Qt::red);
//qDebug() << x << l.y() << cursor_width << l.height();
//p.fillRect(x, l.y(), cursor_width, l.height(), Qt::black);
}
//qDebug() << block_start_cursor.position() << tc.position();
QRect line, all = cursorRect(&line);
if (hasBlockSelection() && (tc.columnNumber() != block_start_cursor.columnNumber())) {
p.setCompositionMode(QPainter::CompositionMode_Difference);
QColor hc = palette().color(QPalette::Highlight);
p.fillRect(all, QColor(255 - hc.red(), 255 - hc.green(), 255 - hc.blue()));
}
if (cursor_state && ui->textCode->hasFocus()) {
line.adjust(0, 1, 0, -1);
p.setCompositionMode(QPainter::CompositionMode_Difference);
p.fillRect(line, Qt::white);
}
}
bool QCodeEdit::hasBlockSelection() const {
return !block_start_cursor.isNull();
}
void QCodeEdit::startBlockSelection() {
if (!hasBlockSelection()) {
QTextCursor tc = textCursor();
block_start_cursor = tc;
block_start_cursor.setPosition(tc.selectionStart());
}}
void QCodeEdit::cancelBlockSelection() {
block_start_cursor = QTextCursor();
}
void QCodeEdit::switchBlockSelection(QKeyEvent * ke) {
bool alt = QApplication::keyboardModifiers().testFlag(Qt::AltModifier);
if (ke) alt = ke->modifiers().testFlag(Qt::AltModifier);
if (alt) {
startBlockSelection();
QTextCursor tc = ui->textCode->textCursor();
QTextCursor::MoveOperation op = QTextCursor::NoMove;
if (!ke) return;
switch (ke->key()) {
case Qt::Key_Left : op = QTextCursor::Left ; break;
case Qt::Key_Right: op = QTextCursor::Right; break;
case Qt::Key_Up : op = QTextCursor::Up ; break;
case Qt::Key_Down : op = QTextCursor::Down ; break;
default: break;
}
if (op != QTextCursor::NoMove) {
tc.movePosition(op);
ui->textCode->setTextCursor(tc);
}
repaintCursor();
} else
cancelBlockSelection();
}
bool QCodeEdit::removeBlockSelection(bool is_del) {
if (!hasBlockSelection()) return false;
QTextCursor tc = ui->textCode->textCursor();
tc.beginEditBlock();
QRect bsr = blockSelectionRect();
if (bsr.width() == 0) {
if (is_del) bsr.setWidth(1);
else if (bsr.x() > 0) bsr.setLeft(bsr.x() - 1);
}
//qDebug() << bsr;
for (int l = bsr.top(); l <= bsr.bottom(); ++l) {
QTextCursor ctc(ui->textCode->document()->findBlockByNumber(l));
ctc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, bsr.left());
if (l != ctc.blockNumber()) continue;
int pos = ctc.position();
ctc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, bsr.width());
if (l != ctc.blockNumber()) {
ctc.setPosition(pos);
ctc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
}
ctc.removeSelectedText();
}
tc.setPosition(tc.block().position() + block_start_cursor.positionInBlock());
ui->textCode->setTextCursor(tc);
tc.endEditBlock();
return true;
}
void QCodeEdit::insertBlockSelection(QString text) {
if (!hasBlockSelection()) return;
QTextCursor tc = ui->textCode->textCursor();
tc.beginEditBlock();
QRect bsr = blockSelectionRect();
if (bsr.width() > 0) removeBlockSelection(false);
for (int l = bsr.top(); l <= bsr.bottom(); ++l) {
QTextCursor ctc(ui->textCode->document()->findBlockByNumber(l));
ctc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, bsr.left());
if (l != ctc.blockNumber()) {
ctc = QTextCursor(ui->textCode->document()->findBlockByNumber(l));
ctc.movePosition(QTextCursor::EndOfLine);
ctc.insertText(QString(bsr.left() - ctc.columnNumber(), QChar(' ')));
}
ctc.insertText(text);
}
//tc.setPosition(tc.block().position() + block_start_cursor.positionInBlock());
//ui->textCode->setTextCursor(tc);
tc.endEditBlock();
}
@@ -835,6 +970,7 @@ void QCodeEdit::scrollDown() {
void QCodeEdit::deleteLine() {
QTextCursor tc = ui->textCode->textCursor();
tc.beginEditBlock();
tc.movePosition(QTextCursor::EndOfLine);
tc.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
bool md = true;
@@ -848,11 +984,13 @@ void QCodeEdit::deleteLine() {
tc.movePosition(QTextCursor::StartOfLine);
if (md) tc.movePosition(QTextCursor::Down);
ui->textCode->setTextCursor(tc);
tc.endEditBlock();
}
void QCodeEdit::copyLineUp() {
QTextCursor tc = ui->textCode->textCursor();
tc.beginEditBlock();
int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se;
QString st_ = tc.selection().toPlainText();
if (st_.endsWith("\n")) {
@@ -875,11 +1013,13 @@ void QCodeEdit::copyLineUp() {
tc.setPosition(se_, QTextCursor::KeepAnchor);
tc.endEditBlock();
ui->textCode->setTextCursor(tc);
tc.endEditBlock();
}
void QCodeEdit::copyLineDown() {
QTextCursor tc = ui->textCode->textCursor();
tc.beginEditBlock();
int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se;
QString st_ = tc.selection().toPlainText();
if (st_.endsWith("\n")) {
@@ -905,6 +1045,7 @@ void QCodeEdit::copyLineDown() {
tc.setPosition(se_, QTextCursor::KeepAnchor);
tc.endEditBlock();
ui->textCode->setTextCursor(tc);
tc.endEditBlock();
}
@@ -921,6 +1062,7 @@ void QCodeEdit::moveLineUp() {
tc.setPosition(ss);
if (!tc.movePosition(QTextCursor::Up))
return;
tc.beginEditBlock();
tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor);
QString l = tc.selectedText();
ss -= l.size(); se -= l.size();
@@ -943,6 +1085,7 @@ void QCodeEdit::moveLineUp() {
tc.setPosition(se_, QTextCursor::KeepAnchor);
tc.endEditBlock();
ui->textCode->setTextCursor(tc);
tc.endEditBlock();
}
@@ -959,6 +1102,7 @@ void QCodeEdit::moveLineDown() {
tc.setPosition(se);
if (!tc.movePosition(QTextCursor::Right))
return;
tc.beginEditBlock();
bool de = false;
if (!tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor)) {
tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
@@ -981,11 +1125,13 @@ void QCodeEdit::moveLineDown() {
tc.setPosition(se_, QTextCursor::KeepAnchor);
tc.endEditBlock();
ui->textCode->setTextCursor(tc);
tc.endEditBlock();
}
void QCodeEdit::indent() {
QTextCursor tc = ui->textCode->textCursor();
tc.beginEditBlock();
int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se;
QString st_ = tc.selection().toPlainText();
if (st_.endsWith("\n")) {
@@ -1005,11 +1151,13 @@ void QCodeEdit::indent() {
tc.setPosition(ss_ + 1);
tc.setPosition(se_, QTextCursor::KeepAnchor);
ui->textCode->setTextCursor(tc);
tc.endEditBlock();
}
void QCodeEdit::deindent() {
QTextCursor tc = ui->textCode->textCursor();
tc.beginEditBlock();
int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se;
QString st_ = tc.selection().toPlainText();
if (st_.endsWith("\n")) {
@@ -1049,6 +1197,7 @@ void QCodeEdit::deindent() {
tc.setPosition(ss_);
tc.setPosition(se_, QTextCursor::KeepAnchor);
ui->textCode->setTextCursor(tc);
tc.endEditBlock();
}
@@ -1056,6 +1205,7 @@ void QCodeEdit::autoIndent() {
QTextCursor tc = ui->textCode->textCursor(), stc = tc;
tc.movePosition(QTextCursor::StartOfLine);
if (!tc.movePosition(QTextCursor::Up)) return;
tc.beginEditBlock();
tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor);
QString line = tc.selectedText(), tabs;
int i = 0;
@@ -1071,6 +1221,7 @@ void QCodeEdit::autoIndent() {
if (tabs.isEmpty()) return;
stc.insertText(tabs);
ui->textCode->setTextCursor(stc);
tc.endEditBlock();
}
@@ -1508,6 +1659,17 @@ void QCodeEdit::textEdit_textChanged() {
void QCodeEdit::textEdit_selectionChanged() {
if (hasBlockSelection()) {
QTextCursor tc = ui->textCode->textCursor();
//qDebug() << block_start_cursor.selectionStart() << tc.selectionEnd();
bool bs = ui->textCode->blockSignals(true);
tc.clearSelection();
ui->textCode->setTextCursor(tc);
ui->textCode->blockSignals(bs);
applyExtraSelection();
return;
}
block_start_cursor = QTextCursor();
es_selected.clear();
QString sf = ui->textCode->textCursor().selectedText();
if (sf.trimmed().isEmpty() || sf.contains("\n")) {