How to force QPlainTextEdit to update its formattings?

59 Views Asked by At

In slot connected to QPlainTextEdit::textChanged() signal I do syntax highlighting with:

struct Format {
    QTextBlock block;
    QList< QTextLayout::FormatRange > format;
};

//! Formats.
QMap< int, Format > formats;

void clearFormats()
{
    for( const auto & f : std::as_const( formats ) )
        f.block.layout()->clearFormats();

    formats.clear();
}

void applyFormats()
{
    for( const auto & f : std::as_const( formats ) )
        f.block.layout()->setFormats( f.format );
}

void setFormat( const QTextCharFormat & format,
    long long int startLine, long long int startColumn,
    long long int endLine, long long int endColumn )
{
    for( auto i = startLine; i <= endLine; ++i )
    {
        formats[ i ].block = editor->document()->findBlockByNumber( i );

        QTextLayout::FormatRange r;
        r.format = format;
        r.start = ( i == startLine ? startColumn : 0 );
        r.length = ( i == startLine ?
            ( i == endLine ? endColumn - startColumn + 1 :
                formats[ i ].block.length() - startColumn ) :
            ( i == endLine ? endColumn + 1 : formats[ i ].block.length() ) );

        formats[ i ].format.push_back( r );
    }
}

On each text change I do the following:

clearFormats();

setFormat( ... ); // When needed in text

applyFormats()

All works as expected when I'm typing a text in editor, syntax highlights.

In my application I have a dialog to set colors for syntax highlighting. On colors change I do above procedure with clearing formats, settings the new with new colors, and applying new formats.

And at this point of time colors remains the same, till I type something in editor.

So my question is: what should I do to force a QPlainTextEdit to apply new formats without typing anything?

Minimal reproducible example:


#include <QPlainTextEdit>
#include <QApplication>
#include <QVBoxLayout>
#include <QPushButton>
#include <QTextCharFormat>
#include <QTextDocument>
#include <QTextBlock>


class Edit
    :   public QPlainTextEdit
{
    Q_OBJECT

public:
    Edit()
    {
        connect( this, &QPlainTextEdit::textChanged,
            this, &Edit::onTextChanged );
    }

    ~Edit() override = default;

public slots:
    void changeColor()
    {
        if( color == Qt::red )
            color = Qt::green;
        else
            color = Qt::red;

        emit textChanged();
    }

private slots:
    void onTextChanged()
    {
        clearFormats();
        setFormat();
    }

private:
    void clearFormats()
    {
        auto b = document()->firstBlock();

        while( b.isValid() )
        {
            b.layout()->clearFormats();

            b = b.next();
        }
    }

    void setFormat()
    {
        auto b = document()->firstBlock();

        QTextCharFormat f;
        f.setForeground( color );

        while( b.isValid() )
        {
            QTextLayout::FormatRange r;
            r.format = f;
            r.start = 0;
            r.length = b.length();

            b.layout()->setFormats( { r } );

            b = b.next();
        }
    }

private:
    QColor color = Qt::red;
};

int main( int argc, char ** argv )
{
    QApplication app( argc, argv );

    QWidget w;
    QVBoxLayout l( &w );

    Edit e;
    l.addWidget( &e );
    QPushButton b;
    b.setText( "Change color" );
    l.addWidget( &b );
    b.connect( &b, &QPushButton::clicked, &e, &Edit::changeColor );

    w.resize( 800, 600 );
    w.show();


    return QApplication::exec();
}

#include "main.moc"
1

There are 1 best solutions below

5
Igor Mironchik On

I solved it. What was needed it's only update a view-port like:

void changeColor()
{
    if( color == Qt::red )
        color = Qt::green;
    else
        color = Qt::red;

    emit textChanged();

    viewport()->update();
}