Search code examples
c++qtvisual-c++qt-creatorqtextbrowser

Track text insertion in QTextBrowser


I am making an application and using QTextBrowser to show messages. It should parse ascii colors, so my class (say MessageBoard) is inheriting from QTextBrowser. I can replace ascii color code and set MessageBoard's text color according to the ascii code before insertion.

But there are many ways of inserting text into the QTextBrowser, so MessageBoard should be able to detect exactly where text is inserted and what is its length.

The problem is, QTextBrowser (through QTextEdit) provides only textChanged signal but there is no way to get where changes happened.

So is there no way to get it or I missing something?

I've solved the problem but this was the issue I was having, (See main.cpp). MessageBoard.h

#ifndef MESSAGEBOARD_H
#define MESSAGEBOARD_H

#include <QTextBrowser>

#define AC_BLACK        "\u001b[30m"
#define AC_RED          "\u001b[31m"
#define AC_GREEN        "\u001b[32m"
#define AC_YELLOW       "\u001b[33m"
#define AC_BLUE         "\u001b[34m"
#define AC_MAGENTA      "\u001b[35m"
#define AC_CYAN         "\u001b[36m"
#define AC_WHITE        "\u001b[37m"
#define AC_RESET        "\u001b[0m"

using AsciiStringPos = std::pair<int /*index*/,int /*length*/>;

class MessageBoard : public QTextBrowser
{
public:
    MessageBoard(QWidget *parent = nullptr);
    void appendText(const QByteArray &text);
    ~MessageBoard();
private:
    std::pair<AsciiStringPos,QColor> find_ascii(const QByteArray &text, int starts_from);
private:
    std::map<QByteArray, QColor> m_colors;
};
#endif // MESSAGEBOARD_H

MessageBoard.cpp

#include "MessageBoard.h"
#include <QRegularExpression>
#include <climits>

MessageBoard::MessageBoard(QWidget *parent)
    : QTextBrowser(parent),
      m_colors({
{QByteArray(AC_BLACK) ,     Qt::black},
{QByteArray(AC_RED) ,       Qt::red},
{QByteArray(AC_GREEN) ,     Qt::green},
{QByteArray(AC_YELLOW) ,    Qt::yellow},
{QByteArray(AC_BLUE) ,      Qt::blue},
{QByteArray(AC_MAGENTA) ,   Qt::magenta},
{QByteArray(AC_CYAN) ,      Qt::cyan},
{QByteArray(AC_WHITE) ,     Qt::white}
               })
{
    m_colors.insert({QByteArray(AC_RESET) , textColor()});
}

void MessageBoard::appendText(const QByteArray &text)
{
    int index = 0;
    QTextCursor text_cursor = textCursor();
    text_cursor.movePosition(QTextCursor::End);
    auto res = find_ascii(text,0);
    while(res.first.first != -1)        //color string's index
    {
        text_cursor.insertText(text.mid(index,res.first.first - index));//append text before the color
        QTextCharFormat format;
        format.setForeground(res.second);   //set color to charformat
        text_cursor.setCharFormat(format);  //set charformat
        index = res.first.first         //color string started from
                + res.first.second;     //color string length
        res = find_ascii(text,index);   //find next color
    }
    text_cursor.insertText(text.mid(index));
}

std::pair<AsciiStringPos, QColor> MessageBoard::find_ascii(const QByteArray &text, int starts_from)
{
    QByteArray first_color;
    int min_index = INT_MAX;
    for(const auto &p : m_colors)
    {
        int index = text.indexOf(p.first,starts_from);
        if(index != -1 && min_index > index)
        {
            min_index = index;
            first_color = p.first;
        }
    }
    if(first_color.isNull())
        return {{-1,0},m_colors[QByteArray(AC_RESET)]};
    else
        return {{min_index,first_color.length()},m_colors[first_color]};
}

MessageBoard::~MessageBoard()
{
}

main.cpp

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MessageBoard w;
    //appendText is manually created, so I can parse text before inserting.
    w.appendText(AC_GREEN "This is written with " AC_RED " Ascii " AC_GREEN " escaped words." AC_RESET);
    //append, can't do the same because I don't know the location where it was inserted.
    w.append(AC_MAGENTA "This won't be written in magenta.");
    w.appendText(AC_CYAN "This will be written in cyan" AC_RESET);
    w.zoomIn(5);
    w.show();
    return a.exec();
}

Output Image


Solution

  • If I understand your request well, I guess you may want to use this signal https://doc.qt.io/qt-5/qtextdocument.html#contentsChange

    You will get access to QTextDocument with this https://doc.qt.io/qt-5/qtextedit.html#document-prop