五 纯代码自定义组件Ⅱ

24 类创建

【Qt开发项目实战——代码编辑器Code Editor Demo——24 纯代码自定义组件Ⅱ——类创建】

  • 新建类MyCodeEditor,选择Widget

  • 修改继承——QPlainTextEdit

  • 修改字体

  • 添加行高亮

  • 添加高亮

  • mycodeeditor.h

#ifndef MYTEXTEDITBYCODE_H
#define MYTEXTEDITBYCODE_H

#include <QWidget>
#include <QTextEdit>
#include <QTextBrowser>
#include <QScrollBar>

class MyTextEditByCode : public QWidget
{
    Q_OBJECT
public:
    explicit MyTextEditByCode(QWidget *parent = nullptr);

private slots:
    void onTextEditHorizontalScrollBarChanged();
    void onTextEditVerticalScrollBarChanged();
    void onScrollBarChanged();
    void onTextBrowserHorizontalScrollBarChanged();

    void onTextChanged();

    void highlightCurrentLine();

private:
    QTextEdit *textEdit ;
    QTextBrowser *textBrowser;
    QScrollBar *scrollBar ;
    QFont mFont;

    void initWidget();
    void initFont();
    void initConnection();
    void initHighlighter();

signals:

};

#endif // MYTEXTEDITBYCODE_H

c++
  • mycodeeditor.cpp
#include "mycodeeditor.h"
#include "myhighlighter.h"

MyCodeEditor::MyCodeEditor(QWidget *parent) : QPlainTextEdit(parent)
{
    //绑定
    initConnection();
    //初始字体
    initFont();

    //高亮
    initHighlighter();

    //行高亮
    highlightCurrentLine();
}


void MyCodeEditor::initConnection()
{
    //cursor
    connect(this,SIGNAL(cursorPositionChanged()),this,SLOT(highlightCurrentLine()));
}

void MyCodeEditor::initFont()
{
    this->setFont(QFont("Consolas",14));
}

void MyCodeEditor::initHighlighter()
{
    new MyHighlighter(document());
}


void MyCodeEditor::highlightCurrentLine()
{
    QList<QTextEdit::ExtraSelection> extraSelections;

    QTextEdit::ExtraSelection selection;
    selection.format.setBackground(QColor(0,100,100,20));
    selection.format.setProperty(QTextFormat::FullWidthSelection,true);
    selection.cursor =textCursor();

    extraSelections.append(selection);
    setExtraSelections(extraSelections);

}
c++

25 绘制行号区域

【Qt开发项目实战——代码编辑器Code Editor Demo——25 纯代码自定义组件Ⅱ——绘制行号区域】

  • mycodeeditor.h
...

class LineNumberWidget;

class MyCodeEditor : public QPlainTextEdit
{
    Q_OBJECT
public:
    ...

    void lineNumberWidgetPaintEvent(QPaintEvent *event);

...
    
protected:
    void resizeEvent(QResizeEvent *event);

private:
    ...
        
    LineNumberWidget *lineNumberWidget;
signals:

};


class LineNumberWidget :public QWidget
{
public:
    LineNumberWidget(MyCodeEditor * editor=nullptr):QWidget(editor){
        codeEditor = editor;
    }

protected:
    void paintEvent(QPaintEvent *event){
        codeEditor->lineNumberWidgetPaintEvent(event);
    }

private:
    MyCodeEditor * codeEditor;
};

...
c++
  • mycodeeditor.cpp
...

#include <QPainter>

MyCodeEditor::MyCodeEditor(QWidget *parent) : QPlainTextEdit(parent)
{
    //行号
    lineNumberWidget = new LineNumberWidget(this);

    ...

    //设置边距
    setViewportMargins(25,0,0,0);
}

...
    
void MyCodeEditor::resizeEvent(QResizeEvent *event)
{
    QPlainTextEdit::resizeEvent(event);
    QRect rect =    contentsRect();
    lineNumberWidget->setGeometry(QRect(rect.left(),rect.top(),25,rect.height()));
}


void MyCodeEditor::lineNumberWidgetPaintEvent(QPaintEvent *event)
{
    QPainter painter(lineNumberWidget);
    painter.fillRect(event->rect(),QColor(100,100,100,100));
}

c++

26 绘制行号文字

【Qt开发项目实战——代码编辑器Code Editor Demo——26 纯代码自定义组件Ⅱ——绘制行号文字】

  • mycodeeditor.h
...
class MyCodeEditor : public QPlainTextEdit
{
    Q_OBJECT
...
private slots:
    ...
    void updateLineNumberWidget(QRect rect,int dy);

...

private:
    ...
    int getLineNumberWidgetWidth();

    ...
}
...
c++
  • mycodeeditor.cpp
...

void MyCodeEditor::initConnection()
{
    ...

    //updateRequest
    connect(this,SIGNAL(updateRequest(QRect, int)),this,SLOT(updateLineNumberWidget(QRect, int)));
}

...

void MyCodeEditor::updateLineNumberWidget(QRect rect, int dy)
{
    if(dy)
        lineNumberWidget->scroll(0,dy);
    else
        lineNumberWidget->update(0,rect.y(),25,rect.height());
}

...

void MyCodeEditor::lineNumberWidgetPaintEvent(QPaintEvent *event)
{
    QPainter painter(lineNumberWidget);
    //绘制行号区域
    painter.fillRect(event->rect(),QColor(100,100,100,20));

    //拿到block
    QTextBlock block = firstVisibleBlock();

    //拿到行号
    int blockNumber =block.blockNumber();

    //拿到block的top
    int top = blockBoundingGeometry(block).translated(contentOffset()).top();

    //拿到block的bottom
    int bottom = top +blockBoundingRect(block).height();

    while(block.isValid()&&top<=event->rect().bottom()){
        //设置画笔颜色
        painter.setPen(Qt::black);
        //绘制文字
        painter.drawText(0,top,25,bottom-top,Qt::AlignRight,QString::number(blockNumber+1));

        //拿到下一个block
        block = block.next();

        top = bottom;
        bottom =  top +blockBoundingRect(block).height();
        blockNumber++;
    }
}
c++

27 行号宽度及高亮

【Qt开发项目实战——代码编辑器Code Editor Demo——27 纯代码自定义组件Ⅱ——行号宽度及高亮】

  • mycodeeditor.h
...
class MyCodeEditor : public QPlainTextEdit
{
    Q_OBJECT
...
        
private slots:
    ...
    void updateLineNumberWidgetWidth();
    ...

...

private:
    ...
    int getLineNumberWidgetWidth();

    ...
}
...
c++
  • mycodeeditor.cpp
...

MyCodeEditor::MyCodeEditor(QWidget *parent) : QPlainTextEdit(parent)
{
    ...

    //设置边距
    updateLineNumberWidgetWidth();
}

void MyCodeEditor::initConnection()
{
    ...
        
    //blockCount
    connect(this,SIGNAL(blockCountChanged(int)),this,SLOT(updateLineNumberWidgetWidth()));

    ...
}

...

int MyCodeEditor::getLineNumberWidgetWidth()
{
    //获取宽度(合适)
    return 8+QString::number(blockCount()+1).length()*fontMetrics().horizontalAdvance(QChar('0'));
}

...

void MyCodeEditor::updateLineNumberWidget(QRect rect, int dy)
{
    if(dy)
        lineNumberWidget->scroll(0,dy);
    else
        lineNumberWidget->update(0,rect.y(),getLineNumberWidgetWidth(),rect.height());
}

void MyCodeEditor::updateLineNumberWidgetWidth()
{
    //设置边距
    setViewportMargins(getLineNumberWidgetWidth(),0,0,0);
}

...

void MyCodeEditor::lineNumberWidgetPaintEvent(QPaintEvent *event)
{
    ...

    //拿到当前的block的top
    int cursorTop = blockBoundingGeometry(textCursor().block()).translated(contentOffset()).top();

    ...

    while(block.isValid()&&top<=event->rect().bottom()){
        //设置画笔颜色
        painter.setPen(cursorTop==top?Qt::black:Qt::gray);
        
        ...
    }
}
c++

28 行号点击事件Ⅰ

【Qt开发项目实战——代码编辑器Code Editor Demo——28 纯代码自定义组件Ⅱ——行号点击事件Ⅰ】

  • mycodeeditor.h
...

class MyCodeEditor : public QPlainTextEdit
{
    Q_OBJECT
public:
    ...

    void lineNumberWidgetMousePressEvent(QMouseEvent *event);

...

};

class LineNumberWidget :public QWidget{
    
...

protected:
    ...
        
    void mousePressEvent(QMouseEvent *event) override{
        //把鼠标点击任务提交给MyCodeEditor
        codeEditor->lineNumberWidgetMousePressEvent(event);
    }
};
...

c++
  • mycodeeditor.cpp
...

void MyCodeEditor::lineNumberWidgetMousePressEvent(QMouseEvent *event)
{
    setTextCursor(QTextCursor(document()->findBlockByLineNumber(event->y()/fontMetrics().height()+verticalScrollBar()->value())));
}
c++

29 行号点击事件Ⅱ

【Qt开发项目实战——代码编辑器Code Editor Demo——29 纯代码自定义组件Ⅱ——行号点击事件Ⅱ】

  • mycodeeditor.cpp
...
    
#include <QScrollBar>
    
...
    
void MyCodeEditor::lineNumberWidgetMousePressEvent(QMouseEvent *event)
{
    setTextCursor(QTextCursor(document()->findBlockByLineNumber(event->y()/fontMetrics().height()+verticalScrollBar()->value())));
}
c++

30 行号滚动事件

【Qt开发项目实战——代码编辑器Code Editor Demo——30 纯代码自定义组件Ⅱ——行号滚动事件】

  • mycodeeditor.h
...

class MyCodeEditor : public QPlainTextEdit
{
    Q_OBJECT
public:
    ...

    void lineNumberWidgetWheelEvent(QWheelEvent *event);
...

};

class LineNumberWidget :public QWidget{
...

protected:
    ...

    void wheelEvent(QWheelEvent *event) override{
        //把滚轮任务提交给MyCodeEditor
        codeEditor->lineNumberWidgetWheelEvent(event);
    }
...
    
};

...
c++
  • mycodeeditor.cpp
...

MyCodeEditor::MyCodeEditor(QWidget *parent) : QPlainTextEdit(parent)
{
    ...

    setLineWrapMode(QPlainTextEdit::NoWrap);
}

...

void MyCodeEditor::lineNumberWidgetWheelEvent(QWheelEvent *event)
{
    if(event->orientation()==Qt::Horizontal){
        horizontalScrollBar()->setValue(horizontalScrollBar()->value()-event->delta());
    }else {
       verticalScrollBar()->setValue(verticalScrollBar()->value()-event->delta());
    }
    event->accept();
}
c++

小结——完整代码

  • mycodeeditor.h
#ifndef MYCODEEDITOR_H
#define MYCODEEDITOR_H

#include <QPlainTextEdit>

class LineNumberWidget;

class MyCodeEditor : public QPlainTextEdit
{
    Q_OBJECT
public:
    explicit MyCodeEditor(QWidget *parent = nullptr);

    void lineNumberWidgetPaintEvent(QPaintEvent * event);

    void lineNumberWidgetMousePressEvent(QMouseEvent *event);

    void lineNumberWidgetWheelEvent(QWheelEvent *event);

private slots:
    void highlightCurrentLine();
    void updateLineNumberWidget(QRect rect, int dy);
    void updateLineNumberWidgetWidth();

protected:
    void resizeEvent(QResizeEvent *event);

private:
    void initConnection();
    void initFont();
    void initHighlighter();
    int getLineNumberWidgetWidth();


    LineNumberWidget * lineNumberWidget;
signals:

};

class LineNumberWidget :public QWidget{
public:
    explicit LineNumberWidget(MyCodeEditor *editor=nullptr):QWidget(editor){
        codeEditor=editor;
    }

protected:
    void  paintEvent(QPaintEvent *event) override{
        //把绘制任务提交给MyCodeEditor
        codeEditor->lineNumberWidgetPaintEvent(event);
    }
    void mousePressEvent(QMouseEvent *event) override{
        //把鼠标点击任务提交给MyCodeEditor
        codeEditor->lineNumberWidgetMousePressEvent(event);
    }

    void wheelEvent(QWheelEvent *event) override{
        //把滚轮任务提交给MyCodeEditor
        codeEditor->lineNumberWidgetWheelEvent(event);
    }

private:
    MyCodeEditor *codeEditor;
};

#endif // MYCODEEDITOR_H
c++
  • mycodeeditor.cpp
#include "mycodeeditor.h"
#include "myhighlighter.h"

#include <QPainter>
#include <QDebug>
#include <QScrollBar>

MyCodeEditor::MyCodeEditor(QWidget *parent) : QPlainTextEdit(parent)
{
    lineNumberWidget = new LineNumberWidget(this);
    //绑定
    initConnection();

    //初始字体
    initFont();

    //高亮
    initHighlighter();

    //行高亮
    highlightCurrentLine();

    //设置边距
    updateLineNumberWidgetWidth();

    setLineWrapMode(QPlainTextEdit::NoWrap);
}

void MyCodeEditor::initConnection()
{
    //cursor
    connect(this,SIGNAL(cursorPositionChanged()),this,SLOT(highlightCurrentLine()));

    //blockCount
    connect(this,SIGNAL(blockCountChanged(int)),this,SLOT(updateLineNumberWidgetWidth()));

    //updateRequest
    connect(this,SIGNAL(updateRequest(QRect, int)),this,SLOT(updateLineNumberWidget(QRect, int)));
}

void MyCodeEditor::initFont()
{
    this->setFont(QFont("Consolas",14));
}

void MyCodeEditor::initHighlighter()
{
    new MyHighlighter(document());
}

int MyCodeEditor::getLineNumberWidgetWidth()
{
    //获取宽度(合适)
    return 8+QString::number(blockCount()+1).length()*fontMetrics().horizontalAdvance(QChar('0'));
}


void MyCodeEditor::highlightCurrentLine()
{
    QList<QTextEdit::ExtraSelection> extraSelections;

    QTextEdit::ExtraSelection selection;
    selection.format.setBackground(QColor(0,100,100,20));
    selection.format.setProperty(QTextFormat::FullWidthSelection,true);
    selection.cursor =textCursor();

    extraSelections.append(selection);
    setExtraSelections(extraSelections);

}

void MyCodeEditor::updateLineNumberWidget(QRect rect, int dy)
{
    if(dy)
        lineNumberWidget->scroll(0,dy);
    else
        lineNumberWidget->update(0,rect.y(),getLineNumberWidgetWidth(),rect.height());
}

void MyCodeEditor::updateLineNumberWidgetWidth()
{
    //设置边距
    setViewportMargins(getLineNumberWidgetWidth(),0,0,0);
}

void MyCodeEditor::resizeEvent(QResizeEvent *event)
{
    QPlainTextEdit::resizeEvent(event);
    lineNumberWidget->setGeometry(0,0,getLineNumberWidgetWidth(),contentsRect().height());
}

void MyCodeEditor::lineNumberWidgetPaintEvent(QPaintEvent *event)
{
    QPainter painter(lineNumberWidget);
    //绘制行号区域
    painter.fillRect(event->rect(),QColor(100,100,100,20));

    //拿到block
    QTextBlock block = firstVisibleBlock();

    //拿到行号
    int blockNumber =block.blockNumber();

    //拿到当前的block的top
    int cursorTop = blockBoundingGeometry(textCursor().block()).translated(contentOffset()).top();

    //拿到block的top
    int top = blockBoundingGeometry(block).translated(contentOffset()).top();

    //拿到block的bottom
    int bottom = top +blockBoundingRect(block).height();

    while(block.isValid()&&top<=event->rect().bottom()){
        //设置画笔颜色
        painter.setPen(cursorTop==top?Qt::black:Qt::gray);
        //绘制文字
        painter.drawText(0,top,getLineNumberWidgetWidth()-3,bottom-top,Qt::AlignRight,QString::number(blockNumber+1));

        //拿到下一个block
        block = block.next();

        top = bottom;
        bottom =  top +blockBoundingRect(block).height();
        blockNumber++;
    }
}

void MyCodeEditor::lineNumberWidgetMousePressEvent(QMouseEvent *event)
{
    setTextCursor(QTextCursor(document()->findBlockByLineNumber(event->y()/fontMetrics().height()+verticalScrollBar()->value())));
}

void MyCodeEditor::lineNumberWidgetWheelEvent(QWheelEvent *event)
{
    if(event->orientation()==Qt::Horizontal){
        horizontalScrollBar()->setValue(horizontalScrollBar()->value()-event->delta());
    }else {
       verticalScrollBar()->setValue(verticalScrollBar()->value()-event->delta());
    }
    event->accept();
}
c++

打赏
  • 微信
  • 支付宝
评论
来发评论吧~
···

歌手: