Qt World Summit: Submit your Presentation

QPlainTextEdit center on custom cursor

  • I use the QTextEdit::ExtraSelection capabilities of the QPlainTextEdit to highlight certain words in the text that is being displayed. Similar to a search function I give the user the ability to jump to the next selection. So far I am able to change the format of the "current selection" and everything works great. The only missing piece is that I scroll the QPlainTextEdit so that the current selection becomes visible.

    My idea was to simply create a copy of the currently set text cursor, set the extra selection cursor to be come the new text cursor, calling QPlainTextEdit::ensureCursorVisible() and then simply restoring the previous cursor. However, the problem is that QPlainTextEdit::setTextCursor() itself already calls QPlainTextEdit::ensureCursorVisible(). This means that when I reset the cursor back to the previous one the plain text edit simply scrolls back again. Just for completeness, this is the code I was using:

    const QTextCursor& currentSelectionCursor = extraSelections().at(myFancyCursorIndex).cursor;
    QTextCursor prevCursor = textCursor();

    Can somebody tell me how I can simply scroll my QPlainTextEdit so that a QTextCursor that is NOT the current text cursor is centered?
    I know that I can use QPlainTextEdit::cursorRect(const QTextCursor& cursor) to get the rectangle of the text cursor relative to the viewport but I can't figure out how I can properly calculate the values required for QPlainTextEdit::verticalScrollBar()::setValue().
    I would prefer that method anyway as ensureCursorVisible() doesn't necessarily center on it.

    Thanks for the help in advance!

  • I assume there is a prettier solution, but you might be able to tell your vertical scrollbar (QAbstractScrollArea::verticalScrollBar()) to scroll only one full line of text at a time (via QAbstractSlider::setRange(int min, int max)), i.e. set the minimum value to 0 and the maximum to the number of lines of text you have. (That probably needs updating, but that shouldn't be too difficult.)
    Then you can call setValue() with the line-number of your currently selected text. If you can't get that number, you could make use of the cursorRect() and the viewports height to calculate it.

  • A (dirty but quick) solution could be something like that:

    // jump to the desired position
    // remember this position
    int scrollPos = verticalScrollBar()->value();
    // reset the cursor position (and scroll back again)
    // scroll again to the remembered position

  • @thEClaw said:

    If you can't get that number, you could make use of the cursorRect() and the viewports height to calculate it.

    My problem is exactly that calculation. I can't figure out the proper way to calculate it. I know that I can query the current height of the viewport but I can't figure out how to get the complete height of the viewport (ie. if there are no scrollbars). I played around with maximumViewportSize() but that returned a ridiculously large number (something like 1600000).
    What I need to archive is to find a value that lies between verticalScrollBar()->minimum() and verticalScrollBar()->maximum().

    @micland Holy... What a simple solution. No idea why I didn't think of that. Thank you very much! This helps a lot!

  • I played around with scrollbar calculation a long time ago and I remember that it was a bit confusing.
    But AFAIR you have to set the QScrollBar::setValue() relative to QScrollBar::maximum() with the same ratio as QAbstractScrollArea::widget()::height() / calculatedPos.
    (not sure - just guessing...)