Referenceqt example
key code
CodeEditor.h file
#ifndef CODEEDITOR_H #define CODEEDITOR_H #include <QPlainTextEdit> #include <QPaintEvent> #include <QContextMenuEvent> #include <QMouseEvent> #include <QMouseEvent> #include <QPainter> #include <QTextBlock> #include <QFontMetrics> #include <QTextCursor> #include <QTextBlockFormat> #include <QTextDocument> #include <QTextCodec> #include <QMenu> #include <QScrollBar> QString GetQString(QByteArray byteArray);//Prevent garbled characters enum TagType {<!-- --> NUMBER=0, MARKROUND,//circle MARKBLOCK //block }; class TagLineNumberArea; class TagDataItem {<!-- --> public: TagDataItem(TagType tagTypeTmp, int widthTmp = 20): tagType(tagTypeTmp), width(widthTmp) {<!-- --> } bool BLineNumbersMark(int number) {<!-- --> //return std::find(vecLineNumbersMark.begin(), vecLineNumbersMark.end(),number) != vecLineNumbersMark.end(); return mapMarkLineColor.find(number) != mapMarkLineColor.end(); } void markerAdd(int line, QColor color=Qt::black) {<!-- --> mapMarkLineColor[line] = color; } QColor getLineColor(int line) {<!-- --> return mapMarkLineColor[line]; } TagType tagType; int positionX; int width; private: std::map<int, QColor> mapMarkLineColor; }; class CodeEditor:public QPlainTextEdit {<!-- --> Q_OBJECT public: CodeEditor(QWidget *paren=0); ~CodeEditor(); virtual void PaintTag(QPainter & amp; painter, int line, int y); void AddTag(TagDataItem* item);//Add tag or line number void UpdateColor(int line, int startIndex, int endIndex, QColor fontColor, QColor backColor); void UpdateColor(int startLine, int endLine, int startColumn, int endColumn, QColor fontColor, QColor backColor);//Update row font or background color void UpdateLineNumberWidth(int lineNumberWidthNew);//Update line number width void UpdateTagWidth(int lineNumberWidthOld, int lineNumberWidth);//Update the width behind the line number void GoToLine(int line);//Jump to line int GetLineNumber(QTextCursor & amp;cursor);//Get the line number void lineNumberAreaPaintEvent(QPaintEvent* event);//Drawing int lineNumberAreaWidth();//Calculate line number width protected: virtual void resizeEvent(QResizeEvent *event)override; private slots: void updateLineNumberAreaWidth(int newBlockCount); void highlightCurrentLine(); void updateLineNumberArea(const QRect &rect, int dy); void updateLineNumber(); public: TagLineNumberArea *tagLineNumberArea; int currentLineNumber; int currentLinePositionY; std::vector<TagDataItem*> vecTagDataItem; QColor backgroundColor; QColor fontColor; QColor fontCurrentColor; int lineNumberPositionX; int lineNumberWidth; int tagCurrentWidth; int spacing; bool bLineNumber; bool bHightCurrentLineNumber; }; class TagLineNumberArea: public QWidget {<!-- --> public: TagLineNumberArea(CodeEditor *editor):QWidget(editor), codeEditor(editor) {<!-- --> } QSize sizeHint()const override {<!-- --> return QSize(codeEditor->lineNumberAreaWidth(), 0); } protected: void paintEvent(QPaintEvent* event)override {<!-- --> codeEditor->lineNumberAreaPaintEvent(event); } public: CodeEditor* codeEditor; }; #endif // CODEEDITOR_H
CodeEditor.cpp file
#include "CodeEditor.h" CodeEditor::CodeEditor(QWidget *parent):QPlainTextEdit(parent) {<!-- --> setMouseTracking(true);//Get mouse movement setWordWrapMode(QTextOption::NoWrap);//Automatically wrap lines, which will cause problems with the obtained line numbers. backgroundColor = QColor(240,240,240); fontColor = QColor(160,160,160); fontCurrentColor = QColor(0,0,0); lineNumberPositionX = 0; tagCurrentWidth = 0; lineNumberWidth = 0; spacing = 5; bLineNumber = false; bHightCurrentLineNumber = true; tagLineNumberArea = new TagLineNumberArea(this); connect(this, & amp;CodeEditor::blockCountChanged, this, & amp;CodeEditor::updateLineNumberAreaWidth); connect(this, & amp;CodeEditor::updateRequest, this, & amp;CodeEditor::updateLineNumberArea); //connect(this, & amp;CodeEditor::cursorPositionChanged, this, & amp;CodeEditor::hightlightCurrentLine); UpdateLineNumberWidth(0); } CodeEditor::~CodeEditor() {<!-- --> } void CodeEditor::PaintTag(QPainter & amp;painter, int line, int y) {<!-- --> for (TagDataItem* item:vecTagDataItem) {<!-- --> switch (item->tagType) {<!-- --> case NUMBER: break; case MARKROUND: if(item->BLineNumbersMark(line)) {<!-- --> painter.setPen(QColor(item->getLineColor(line))); painter.setBrush(QColor(item->getLineColor(line))); int w = item->width < fontMetrics().height() ? item->width : fontMetrics().height(); painter.drawEllipse(item->positionX + item->width/2 - w/2, y + fontMetrics().height()/2 - w/2, w, w); } break; case MARKBLOCK: if(item->BLineNumbersMark(line)) {<!-- --> painter.setPen(QColor(item->getLineColor(line))); painter.setBrush(QColor(item->getLineColor(line))); painter.drawRect(item->positionX, y, item->width, fontMetrics().height()); } break; default: break; } } } void CodeEditor::AddTag(TagDataItem *item) {<!-- --> item->positionX = tagCurrentWidth + spacing; vecTagDataItem.push_back(item); tagCurrentWidth = tagCurrentWidth + spacing + item->width; if(!bLineNumber & amp; & amp; item->tagType == NUMBER) {<!-- --> lineNumberPositionX = item->positionX; lineNumberWidth = item->width; bLineNumber = true; } } void CodeEditor::UpdateColor(int line, int startIndex, int endIndex, QColor fontColor, QColor backColor) {<!-- --> QTextCursor cursor = this->textCursor(); //Move row cursor.movePosition(QTextCursor::Start); for(int i=1; i<line; i + + ) {<!-- --> cursor.movePosition(QTextCursor::Down); } //move column cursor.movePosition(QTextCursor::StartOfBlock); for(int i=1; i<startIndex; i + + ) {<!-- --> cursor.movePosition(QTextCursor::NextCharacter); } if(endIndex == 0) {<!-- --> cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); } else {<!-- --> for(int i=1; i<endIndex; i + + ) {<!-- --> cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); } } QTextCharFormat fmt; fmt.setForeground(QBrush(fontColor));//Font color fmt.setBackground(QBrush(backColor));//Background color cursor.setCharFormat(fmt); } void CodeEditor::UpdateColor(int startLine, int endLine, int startColumn, int endColumn, QColor fontColor, QColor backColor) {<!-- --> QTextCursor cursor = this->textCursor(); //Move row cursor.movePosition(QTextCursor::Start); for(int i=1; i<startLine; i + + ) {<!-- --> cursor.movePosition(QTextCursor::Down); } //move column cursor.movePosition(QTextCursor::StartOfBlock); for(int i=1; i<startColumn; i + + ) {<!-- --> cursor.movePosition(QTextCursor::NextCharacter); } //Move row cursor.movePosition(QTextCursor::Start); for(int i=startLine; i<endLine; i + + ) {<!-- --> cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor); } //move column cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); for(int i=0; i<endColumn; i + + ) {<!-- --> cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); } QTextCharFormat fmt; fmt.setForeground(QBrush(fontColor));//Font color fmt.setBackground(QBrush(backColor));//Background color cursor.setCharFormat(fmt); } void CodeEditor::UpdateLineNumberWidth(int lineNumberWidthNew) {<!-- --> int width = tagCurrentWidth - lineNumberWidth + lineNumberWidthNew; tagCurrentWidth = width; UpdateTagWidth(lineNumberWidth, lineNumberWidthNew); lineNumberWidth = lineNumberWidthNew; } void CodeEditor::UpdateTagWidth(int lineNumberWidthOld, int lineNumberWidth) {<!-- --> bool numberAfterTag = false; for(TagDataItem* item: vecTagDataItem) {<!-- --> if(item->tagType == NUMBER) {<!-- --> numberAfterTag = true; } if(numberAfterTag) {<!-- --> item->positionX = item->positionX - lineNumberWidthOld + lineNumberWidth; } } } void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */) {<!-- --> setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); } void CodeEditor::hightlightCurrentLine() {<!-- --> QList<QTextEdit::ExtraSelection> extraSelections; if(!isReadOnly()) {<!-- --> QTextEdit::ExtraSelection selection; QColor lineColor = QColor(Qt::yellow).lighter(160); selection.format.setBackground(lineColor); selection.format.setProperty(QTextFormat::FullWidthSelection, true); selection.cursor = textCursor(); selection.cursor.clearSelection(); extraSelections.append(selection); } setExtraSelections(extraSelections); } void CodeEditor::updateLineNumberArea(const QRect &rect, int dy) {<!-- --> if(dy) tagLineNumberArea->scroll(0, dy); else tagLineNumberArea->update(0, rect.y(), tagLineNumberArea->width(), rect.height()); if (rect.contains(viewport()->rect())) updateLineNumberAreaWidth(0); } void CodeEditor::updateLineNumber() {<!-- --> tagLineNumberArea->update(); } void CodeEditor::GoToLine(int line) {<!-- --> QTextCursor textCursor = this->textCursor(); int position = document()->findBlockByNumber(line - 1).position(); textCursor.setPosition(position, QTextCursor::MoveAnchor); setTextCursor(textCursor); this->verticalScrollBar()->setValue(line-1); } int CodeEditor::GetLineNumber(QTextCursor & amp;cursor) {<!-- --> QTextLayout *layout = cursor.block().layout(); int pos = cursor.position() - cursor.block().position(); int line = layout->lineForTextPosition(pos).lineNumber() + cursor.block().firstLineNumber(); return line; } int CodeEditor::lineNumberAreaWidth() {<!-- --> if(!bLineNumber) return tagCurrentWidth; int digits = 1; int max = qMax(1, blockCount()); while(max >= 10) {<!-- --> max /=10; + + digits; } int lineNumberWidth = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9'))*digits; UpdateLineNumberWidth(lineNumberWidth); return tagCurrentWidth; } void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event) {<!-- --> //Current row QTextCursor cursor = textCursor(); int line = GetLineNumber(cursor); currentLineNumber = line; currentLinePositionY = -1; //background QPainter painter(tagLineNumberArea); painter.setRenderHint(QPainter::Antialiasing, true); painter.fillRect(event->rect(), backgroundColor); // QTextBlock block = firstVisibleBlock(); int blockNumber = block.blockNumber(); int top = qRound(blockBoundingGeometry(block).translated(contentOffset()).top()); int bottom = top + qRound(blockBoundingRect(block).height()); while(block.isValid() & amp; & amp; top <= event->rect().bottom()) {<!-- --> if(block.isVisible() & amp; & amp; bottom >= event->rect().top()) {<!-- --> QString number = QString::number(blockNumber + 1); if(blockNumber == line) currentLinePositionY = top;//Record the current position //Record the line number if(bLineNumber) {<!-- --> if(blockNumber == line & amp; & amp; bHightCurrentLineNumber) {<!-- --> painter.setPen(fontCurrentColor);//Highlight the current line number QFont ft = painter.font(); ft.setBold(true); painter.setFont(ft); } else {<!-- --> painter.setPen(fontColor); } painter.drawText(lineNumberPositionX, top, lineNumberWidth, fontMetrics().height(), Qt::AlignRight, number); } //Draw tag PaintTag(painter, blockNumber, top); } block = block.next(); top = bottom; bottom = top + qRound(blockBoundingGeometry(block).height()); + + blockNumber; } } void CodeEditor::resizeEvent(QResizeEvent *e) {<!-- --> QPlainTextEdit::resizeEvent(e); QRect cr = contentsRect(); tagLineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); } QString GetQString(QByteArray byteArray) {<!-- --> QTextCodec::ConverterState state; QTextCodec *codec = QTextCodec::codecForName("UTF-8"); QString qstr = codec->toUnicode(byteArray.constData(), byteArray.size(), & amp;state); if(state.invalidChars > 0) qstr = QTextCodec::codecForName("GBK")->toUnicode(byteArray); else {<!-- --> qstr = byteArray; } return qstr; }
mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include "CodeEditor.h" #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {<!-- --> CodeEditor *edit = new CodeEditor; TagDataItem* item = new TagDataItem(MARKBLOCK, 10); item->markerAdd(2, Qt::blue); item->markerAdd(5, Qt::yellow); item->markerAdd(526, Qt::red); item->markerAdd(527, Qt::black); edit->AddTag(item); TagDataItem *itemNumber = new TagDataItem(NUMBER, 20); edit->AddTag(itemNumber); TagDataItem *item2 = new TagDataItem(MARKBLOCK, 10); item2->markerAdd(5, Qt::blue); item2->markerAdd(6, Qt::yellow); item2->markerAdd(7, Qt::red); item2->markerAdd(8, Qt::red); item2->markerAdd(526, Qt::red); item2->markerAdd(527, Qt::red); edit->AddTag(item2); this->setCentralWidget(edit); QFont ft; ft.setFamily("Microsoft Yahei"); ft.setPointSize(10); edit->setFont(ft); QFile file("C:/Data/test.cpp"); qDebug() << file.exists(); if(file.open(QIODevice::ReadOnly)) {<!-- --> edit->appendPlainText(file.readAll()); file.close(); } edit->GoToLine(520); edit->UpdateColor(1,3,2, 5,Qt::red, Qt::yellow); edit->UpdateColor(2,3,2, 5,Qt::red, Qt::yellow); } MainWindow::~MainWindow() {<!-- --> }