qtqtexteditqtextdocument

QTextLayout::drawCursor() does not work when subclassing QAbstractTextDocumentLayout


currently QTextEdit and QPlainTextEdit does not meet my requirements, so I need to subclass QAbstractTextDocumentLayout to provide custom layout of the document. I reference QPlainTextDocumentLayout and QTextDocumentLayout a lot, and finally got a simple layout to display the text. However, I couldn't see the cursor in QTextEdit, which should be blinking. I need help to figure this out.

I am using Qt 5.9.1. The simple project is here. The draw() function of VTextDocumentLayout looks like this:

void VTextDocumentLayout::draw(QPainter *p_painter, const PaintContext &p_context)
{
    qDebug() << "VTextDocumentLayout draw()" << p_context.clip << p_context.cursorPosition << p_context.selections.size();
    // Find out the blocks.
    int first, last;
    blockRangeFromRect(p_context.clip, first, last);
    if (first == -1) {
        return;
    }

    QTextDocument *doc = document();
    QPointF offset(m_margin, m_blocks[first].m_offset);
    QTextBlock block = doc->findBlockByNumber(first);
    QTextBlock lastBlock = doc->findBlockByNumber(last);
    qreal maximumWidth = m_width;

    while (block.isValid()) {
        const BlockInfo &info = m_blocks[block.blockNumber()];
        const QRectF &rect = info.m_rect;
        QTextLayout *layout = block.layout();

        if (!block.isVisible()) {
            offset.ry() += rect.height();
            if (block == lastBlock) {
                break;
            }

            block = block.next();
            continue;
        }

        QTextBlockFormat blockFormat = block.blockFormat();
        QBrush bg = blockFormat.background();
        if (bg != Qt::NoBrush) {
            fillBackground(p_painter, rect, bg);
        }

        auto selections = formatRangeFromSelection(block, p_context.selections);

        QPen oldPen = p_painter->pen();
        p_painter->setPen(p_context.palette.color(QPalette::Text));

        layout->draw(p_painter,
                     offset,
                     selections,
                     p_context.clip.isValid() ? p_context.clip : QRectF());

        // Draw the cursor.
        int blpos = block.position();
        int bllen = block.length();
        bool drawCursor = p_context.cursorPosition >= blpos
                          && p_context.cursorPosition < blpos + bllen;

        Q_ASSERT(p_context.cursorPosition >= -1);
        if (drawCursor) {
            int cpos = p_context.cursorPosition;
            cpos -= blpos;

            qDebug() << "draw cursor" << block.blockNumber() << blpos << bllen << p_context.cursorPosition << cpos;
            layout->drawCursor(p_painter, offset, cpos);
        }

        p_painter->setPen(oldPen);

        offset.ry() += rect.height();
        if (block == lastBlock) {
            break;
        }

        block = block.next();
    }
}

I called layout->drawCursor() to draw the cursor but this function seems to do nothing.

Any help is appreciated! Thanks!

Update: Add the log as following:

VTextDocumentLayout draw() QRectF(67,0 9x13) 19 0
block range 0 1
draw cursor 1 5 15 19 14
blockBoundingRect() 1 13 QRectF(0,0 75x17)
VTextDocumentLayout draw() QRectF(67,0 9x13) -1 0
block range 0 1
blockBoundingRect() 1 13 QRectF(0,0 75x17)
VTextDocumentLayout draw() QRectF(67,0 9x13) 19 0

When running this project in Linux, I coundn't see the cursor. However, when running it in Windows, I could see the cursor but not blinking.

Update: It seems that QTextEdit pass a wrong clip rect to the layout. If I just inserted one line of text (only one block within the document), I could see the blinking cursor. Quite strange!!!


Solution

  • When subclassing QAbstractTextDocumentLayout, blockBoundingRect() should return the geometry of the block, not just the rect as what QPlainTextDocumentLayout does.

    In one word, QPlainTextDocumentLayout provides a BAD example for subsclassing QAbstractTextDocumentLayout.