diff --git a/libs/widgets/qcodeedit.cpp b/libs/widgets/qcodeedit.cpp index b9bb956..3626293 100644 --- a/libs/widgets/qcodeedit.cpp +++ b/libs/widgets/qcodeedit.cpp @@ -21,18 +21,46 @@ Q_DECLARE_METATYPE(QTextCursor) Q_DECLARE_METATYPE(QCodeEdit::ACEntry) + +class _QCE_Viewport: public QWidget { +public: + _QCE_Viewport(QWidget * p = 0): QWidget(p) { + setObjectName("__qcodeedit_viewport__"); + //setCursor(Qt::IBeamCursor); + } + + void paintEvent(QPaintEvent * e) override { + //qDebug() << "paint" << geometry(); + //QPainter p(this); + //p.fillRect(rect(), Qt::red); + //QWidget::paintEvent(e); + ce->drawCursor(); + } + + bool event(QEvent * e) override { + //qDebug() << "event" << e; + return QWidget::event(e); + } + QCodeEdit * ce; +}; + + 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; + _first = true; qRegisterMetaType(); qRegisterMetaType(); ui = new Ui::QCodeEdit(); ui->setupUi(this); + overlay = new _QCE_Viewport(ui->textCode->viewport()); + overlay->ce = this; + overlay->show(); ui->widgetSearch->hide(); ui->comboSearch->installEventFilter(this); ui->comboReplace->installEventFilter(this); - prev_lc = auto_comp_pl = cur_search_ind = pos_press = pos_el_press = -1; - timer = 0; - _ignore_focus_out = _destructor = _replacing = false; - _first = true; es_line.format.setBackground(QColor(240, 245, 240)); es_line.format.setProperty(QTextFormat::FullWidthSelection, true); es_cursor.format.setBackground(QColor(220, 255, 200)); @@ -62,7 +90,8 @@ QCodeEdit::QCodeEdit(QWidget * parent): QWidget(parent) { help_visible = true; completer = new QCodeEditCompleter(); - ui->textCode->setCursorWidth(qMax(qRound(fontHeight() / 10.), 1)); + cursor_width = qMax(qRound(fontHeight() / 10.), 1); + ui->textCode->setCursorWidth(0); ui->textCode->viewport()->setMouseTracking(true); ui->textLines->viewport()->setAutoFillBackground(false); ui->textLines->viewport()->setCursor(Qt::ArrowCursor); @@ -122,6 +151,8 @@ QCodeEdit::QCodeEdit(QWidget * parent): QWidget(parent) { connect(completer, SIGNAL(gotoHRef(QCodeEdit::ACEntry)), this, SLOT(gotoHelpHRef(QCodeEdit::ACEntry))); connect(ui->textCode->verticalScrollBar(), SIGNAL(valueChanged(int)), ui->textLines->verticalScrollBar(), SLOT(setValue(int))); connect(ui->textCode->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(hideHelp())); + connect(ui->textCode->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(resizeOverlay())); + connect(ui->textCode->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(resizeOverlay())); connect(ui->textCode, SIGNAL(textChanged()), this, SLOT(textEdit_textChanged())); connect(ui->textCode, SIGNAL(textChanged()), this, SIGNAL(textChanged())); connect(ui->textCode, SIGNAL(cursorPositionChanged()), this, SLOT(textEdit_cursorPositionChanged())); @@ -132,7 +163,8 @@ QCodeEdit::QCodeEdit(QWidget * parent): QWidget(parent) { connect(qApp, &QGuiApplication::focusWindowChanged, this, [this](QWindow*w){if (w == 0) {hideHelp(); completer->hide();}}); #endif updateLines(); - + timer_blink = startTimer(QApplication::cursorFlashTime() / 2); + registerAutoCompletitionClass(-1, QCodeEdit::Keyword, "Words", QIcon(":/icons/code-word.png")); } @@ -173,7 +205,8 @@ void QCodeEdit::setDocument(QTextDocument * doc) { if (!qobject_cast(doc->documentLayout())) doc->setDocumentLayout(new QPlainTextDocumentLayout(doc)); ui->textCode->setDocument(doc); - ui->textCode->setCursorWidth(qMax(qRound(fontHeight() / 10.), 1)); + cursor_width = qMax(qRound(fontHeight() / 10.), 1); + //ui->textCode->setCursorWidth(qMax(qRound(fontHeight() / 10.), 1)); setShowSpaces(spaces_); if (doc->property("_cursor").isValid()) { setTextCursor(doc->property("_cursor").value()); @@ -436,6 +469,9 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) { gotoLink(); } } + if (e->type() == QEvent::Paint) { + resizeOverlay(); + } return QWidget::eventFilter(o, e); } if (o == ui->textCode) { @@ -592,11 +628,17 @@ void QCodeEdit::showEvent(QShowEvent * ) { } -void QCodeEdit::timerEvent(QTimerEvent * ) { - parse(); - emit parseRequest(); - killTimer(timer); - timer = 0; +void QCodeEdit::timerEvent(QTimerEvent * e) { + if (e->timerId() == timer_parse) { + parse(); + emit parseRequest(); + killTimer(timer_parse); + timer_parse = 0; + } + if (e->timerId() == timer_blink) { + cursor_state = !cursor_state; + repaintCursor(); + } } @@ -705,6 +747,37 @@ int QCodeEdit::searchIndFromCursor() { } +void QCodeEdit::repaintCursor() { + QRect r = ui->textCode->cursorRect(ui->textCode->textCursor()); + r.setWidth(cursor_width); + //r.translate(-cursor_width, 0); + overlay->update(r); +} + + +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); + } + } +} + + void QCodeEdit::searchAll() { QString st = ui->comboSearch->currentText(); es_search_list.clear(); @@ -1014,7 +1087,7 @@ void QCodeEdit::showLink() { return; } es_link.cursor = tc; - ui->textCode->viewport()->setCursor(tc.isNull() ? Qt::IBeamCursor : Qt::PointingHandCursor); + overlay->setCursor(tc.isNull() ? Qt::IBeamCursor : Qt::PointingHandCursor); applyExtraSelection(); //qDebug() << "showLink" << tc.selectedText() << link_entry.type << link_entry.name; } @@ -1022,7 +1095,7 @@ void QCodeEdit::showLink() { void QCodeEdit::hideLink() { es_link.cursor = QTextCursor(); - ui->textCode->viewport()->setCursor(Qt::IBeamCursor); + overlay->setCursor(Qt::IBeamCursor); link_entry = ACEntry(); applyExtraSelection(); //qDebug() << "hideLink"; @@ -1074,8 +1147,8 @@ void QCodeEdit::setText(const QString & t) { void QCodeEdit::updateLines() { - if (timer > 0) killTimer(timer); - timer = startTimer(500); + if (timer_parse > 0) killTimer(timer_parse); + timer_parse = startTimer(500); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) # if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) ui->textCode->setTabStopDistance(ui->textCode->fontMetrics().horizontalAdvance(" ")); @@ -1205,6 +1278,12 @@ void QCodeEdit::gotoHelpHRef(QCodeEdit::ACEntry e) { } +void QCodeEdit::resizeOverlay() { + if (overlay) + overlay->setGeometry(ui->textCode->viewport()->geometry()); +} + + void QCodeEdit::hideHelp() { help_entry = ACEntry(); widget_help->hide(); @@ -1414,6 +1493,10 @@ void QCodeEdit::textEdit_cursorPositionChanged() { es_line.cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); highlightBrackets(); applyExtraSelection(); + if (timer_blink) killTimer(timer_blink); + timer_blink = startTimer(QApplication::cursorFlashTime() / 2); + cursor_state = true; + repaintCursor(); } diff --git a/libs/widgets/qcodeedit.h b/libs/widgets/qcodeedit.h index 9521df0..1cd2be2 100644 --- a/libs/widgets/qcodeedit.h +++ b/libs/widgets/qcodeedit.h @@ -32,6 +32,7 @@ namespace Ui { } class QCodeEditCompleter; +class _QCE_Viewport; class QAD_WIDGETS_EXPORT QCodeEdit: public QWidget @@ -44,6 +45,7 @@ class QAD_WIDGETS_EXPORT QCodeEdit: public QWidget Q_PROPERTY(QFont editorFont READ editorFont WRITE setEditorFont) friend class QCodeEditCompleter; + friend class _QCE_Viewport; public: QCodeEdit(QWidget * parent = 0); @@ -139,14 +141,16 @@ private: QCodeEditCompleter * completer; IconedLabel * lbl_help[3]; QFrame * widget_help; + _QCE_Viewport * overlay; QTextEdit::ExtraSelection es_line, es_cursor, es_bracket, es_range, es_search, es_link; QList es_selected, es_custom, es_brackets, es_search_list; QMap ac_classes; QStringList cursor_scope; ACEntry link_entry, help_entry; - int prev_lc, auto_comp_pl, timer, cur_search_ind, pos_press, pos_el_press; + int prev_lc, auto_comp_pl, timer_parse, timer_blink, cur_search_ind, pos_press, pos_el_press; + int cursor_width; bool spaces_, _ignore_focus_out, _first, _destructor, _replacing; - bool word_complete, help_visible; + bool word_complete, help_visible, cursor_state; bool eventFilter(QObject * o, QEvent * e) override; void showEvent(QShowEvent * ) override; @@ -158,11 +162,14 @@ private: void clearSearch(); void moveToSearch(); int searchIndFromCursor(); + void repaintCursor(); + void drawCursor(); ACEntry findEntryOnCursor(QTextCursor tc, int arg = -1, ACClass * acc = 0, QPair * scope = 0); private slots: void _activateLink(QCodeEdit::ACEntry e) {linkClicked(e);} void gotoHelpHRef(QCodeEdit::ACEntry e); + void resizeOverlay(); void syncScrolls(); void scrollUp(); void scrollDown();