#include "TransmitTextEdit.h" #include void setTextEditFont(QTextEdit *edit, QFont font){ // all uppercase font.setCapitalization(QFont::AllUppercase); edit->setFont(font); edit->setFontFamily(font.family()); edit->setFontItalic(font.italic()); edit->setFontPointSize(font.pointSize()); edit->setFontUnderline(font.underline()); edit->setFontWeight(font.weight()); auto d = edit->document(); d->setDefaultFont(font); edit->setDocument(d); auto c = edit->textCursor(); c.select(QTextCursor::Document); auto cf = c.blockCharFormat(); cf.setFont(font); cf.setFontCapitalization(QFont::AllUppercase); c.mergeBlockCharFormat(cf); edit->updateGeometry(); } void setTextEditStyle(QTextEdit *edit, QColor fg, QColor bg, QFont font){ edit->setStyleSheet(QString("QTextEdit { color:%1; background: %2; %3; }").arg(fg.name()).arg(bg.name()).arg(font_as_stylesheet(font))); //QTimer::singleShot(10, nullptr, [edit, fg, bg](){ QPalette p = edit->palette(); p.setColor(QPalette::Base, bg); p.setColor(QPalette::Active, QPalette::Base, bg); p.setColor(QPalette::Disabled, QPalette::Base, bg); p.setColor(QPalette::Inactive, QPalette::Base, bg); p.setColor(QPalette::Text, fg); p.setColor(QPalette::Active, QPalette::Text, fg); p.setColor(QPalette::Disabled, QPalette::Text, fg); p.setColor(QPalette::Inactive, QPalette::Text, fg); edit->setBackgroundRole(QPalette::Base); edit->setForegroundRole(QPalette::Text); edit->setPalette(p); edit->updateGeometry(); edit->update(); //}); } void highlightBlock(QTextBlock block, QFont font, QColor foreground, QColor background){ QTextCursor cursor(block); // Set background color QTextBlockFormat blockFormat = cursor.blockFormat(); blockFormat.setBackground(background); cursor.setBlockFormat(blockFormat); // Set font /* for (QTextBlock::iterator it = cursor.block().begin(); !(it.atEnd()); ++it) { QTextCharFormat charFormat = it.fragment().charFormat(); charFormat.setFont(font); charFormat.setFontCapitalization(QFont::AllUppercase); charFormat.setForeground(QBrush(foreground)); QTextCursor tempCursor = cursor; tempCursor.setPosition(it.fragment().position()); tempCursor.setPosition(it.fragment().position() + it.fragment().length(), QTextCursor::KeepAnchor); tempCursor.setCharFormat(charFormat); } */ cursor.select(QTextCursor::BlockUnderCursor); auto charFormat = cursor.charFormat(); charFormat.setFont(font); charFormat.setFontCapitalization(QFont::AllUppercase); charFormat.setForeground(QBrush(foreground)); cursor.setCharFormat(charFormat); } TransmitTextEdit::TransmitTextEdit(QWidget *parent): QTextEdit(parent), m_sent { 0 }, m_protected { false } { connect(this, &QTextEdit::selectionChanged, this, &TransmitTextEdit::on_selectionChanged); connect(this, &QTextEdit::cursorPositionChanged, this, &TransmitTextEdit::on_selectionChanged); connect(this->document(), &QTextDocument::contentsChange, this, &TransmitTextEdit::on_textContentsChanged); installEventFilter(this); } void TransmitTextEdit::setCharsSent(int n){ // never can send more than the document length n = qMin(n, document()->characterCount()); // update sent display auto c = textCursor(); c.movePosition(QTextCursor::Start); c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, n); // keep track of sent text m_textSent = c.selectedText().toUpper(); m_sent = n; // highlight the sent text //highlightCharsSent(); highlight(); } // override QString TransmitTextEdit::toPlainText() const { return QTextEdit::toPlainText().toUpper(); } // override void TransmitTextEdit::setPlainText(const QString &text){ QTextEdit::setPlainText(text); m_textSent.clear(); m_sent = 0; } // void TransmitTextEdit::replaceUnsentText(const QString &text){ auto c = textCursor(); c.movePosition(QTextCursor::Start); c.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, m_sent); c.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); c.removeSelectedText(); c.insertText(text); } // void TransmitTextEdit::replacePlainText(const QString &text){ auto c = textCursor(); c.movePosition(QTextCursor::Start); c.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); c.removeSelectedText(); c.insertText(text); } // void TransmitTextEdit::setFont(QFont f){ m_font = f; // then rehighlight highlight(); } // void TransmitTextEdit::setFont(QFont f, QColor fg, QColor bg){ m_font = f; m_fg = fg; m_bg = bg; // then rehighlight highlight(); } // override void TransmitTextEdit::clear(){ QTextEdit::clear(); m_textSent.clear(); m_sent = 0; } void TransmitTextEdit::setProtected(bool protect){ m_protected = protect; } bool TransmitTextEdit::cursorShouldBeProtected(QTextCursor c){ int start = c.selectionStart(); int end = c.selectionEnd(); if(end < start){ int x = end; end = start; start = x; } //qDebug() << "selection" << start << end << m_sent; if(m_sent && start <= m_sent){ //qDebug() << "selection in protected zone" << start << "<=" << m_sent; return true; } else { return false; } } // slot void TransmitTextEdit::on_selectionChanged(){ auto c = textCursor(); auto shouldProtect = cursorShouldBeProtected(c); if(shouldProtect){ blockSignals(true); { int end = c.selectionEnd(); c.movePosition(QTextCursor::Start); c.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, m_sent); c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, qMax(0, end-m_sent)); setTextCursor(c); } blockSignals(false); } setProtected(shouldProtect); } // slot void TransmitTextEdit::on_textContentsChanged(int /*pos*/, int rem, int add){ if(rem == 0 && add == 0){ return; } auto text = toPlainText(); if(text != m_lastText){ //qDebug() << "text changed" << pos << rem << add << "from" << m_lastText << "to" << text; highlight(); //qDebug() << "sent:" << sentText(); //qDebug() << "unsent:" << unsentText(); m_dirty = true; m_lastText = text; } } void TransmitTextEdit::highlightBase(){ auto d = document(); if(!d){ return; } auto block = d->firstBlock(); while(block.isValid()){ highlightBlock(block, m_font, m_fg, m_bg); block = block.next(); } } void TransmitTextEdit::highlightCharsSent(){ if(!m_sent){ return; } auto d = document(); if(!d){ return; } // highlight sent text auto c = textCursor(); if(c.isNull()){ return; } c.movePosition(QTextCursor::Start); c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m_sent); auto ch = c.charFormat(); ch.setFontStrikeOut(true); c.mergeCharFormat(ch); } void TransmitTextEdit::highlight(){ highlightBase(); highlightCharsSent(); } QTextCursor::MoveOperation movementKeyEventToMoveOperation(QKeyEvent *e){ 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; } else if (e == QKeySequence::SelectPreviousChar) { op = QTextCursor::Left; } else if (e == QKeySequence::SelectNextWord) { op = QTextCursor::WordRight; } else if (e == QKeySequence::SelectPreviousWord) { op = QTextCursor::WordLeft; } else if (e == QKeySequence::SelectStartOfLine) { op = QTextCursor::StartOfLine; } else if (e == QKeySequence::SelectEndOfLine) { op = QTextCursor::EndOfLine; } else if (e == QKeySequence::SelectStartOfBlock) { op = QTextCursor::StartOfBlock; } else if (e == QKeySequence::SelectEndOfBlock) { op = QTextCursor::EndOfBlock; } else if (e == QKeySequence::SelectStartOfDocument) { op = QTextCursor::Start; } else if (e == QKeySequence::SelectEndOfDocument) { op = QTextCursor::End; } else if (e == QKeySequence::SelectPreviousLine) { op = QTextCursor::Up; } else if (e == QKeySequence::SelectNextLine) { op = QTextCursor::Down; } 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; } return op; } bool isMovementKeyEvent(QKeyEvent * k){ return movementKeyEventToMoveOperation(k) != QTextCursor::NoMove; } bool TransmitTextEdit::eventFilter(QObject */*o*/, QEvent *e){ if(e->type() != QEvent::KeyPress){ return false; } // -1. don't filter the escape key, return key, or enter key here QKeyEvent *k = static_cast(e); if(k->key() == Qt::Key_Escape || k->key() == Qt::Key_Return || k->key() == Qt::Key_Enter){ return false; } auto c = textCursor(); auto c2 = QTextCursor(c); c2.movePosition(movementKeyEventToMoveOperation(k)); bool shouldBeProtected = cursorShouldBeProtected(c2); // 0. only filter when in a protected range // 0a. but only if we're not moving/deleting _into_ the protected range :/ if(!isProtected() && !shouldBeProtected){ return false; } // 1. do not filter movement sequences if(isMovementKeyEvent(k)){ return false; } // 2. if on the edge, do not filter if not a backspace int start = qMin(c.selectionStart(), c.selectionEnd()); if(start == m_sent && k->key() != Qt::Key_Backspace){ return false; } // 3. if on the edge, do not filter if a backspace and there is text selected int end = qMax(c.selectionStart(), c.selectionEnd()); if(start == m_sent && start != end && k->key() == Qt::Key_Backspace){ return false; } return true; }