I've researched this extensively but haven't found a satisfactory solution yet:
How do I append text at the end of QTextEdit widget without triggering a scroll to the bottom of the widget when either of these conditions is met:
- The user has selected some text.
- The user has scrolled away from the bottom.
(In all other cases, a scroll to the bottom of the QTextEdit widget should be triggered.)
Here is the code I'm currently using to append text
at the bottom of a QTextEdit widget
:
const QTextCursor old_cursor = widget.textCursor();
widget.moveCursor(QTextCursor::End);
widget.textCursor().insertText(text);
if (old_cursor.hasSelection())
widget.setTextCursor(old_cursor);
else widget.moveCursor(QTextCursor::End);
This partially takes care of condition 1: the problem is that the view will still scroll until only the last line of the selection is visible, at which point it will indeed stop scrolling.
Condition 2 is not taken care of at all: some posts suggest to save the position of the vertical scrollbar and restore it after the text was appended, however I don't think this is correct since the scrollbar should move upward when text is appended, even though the view stays still.
Note that I'm using QTextCursor::insertText()
instead of QTextEdit::append()
because I need to adjust the color of the text being appended, regardless of whether the user has selected text or not.
Update: Here is the code I ended up with, thanks to Pavel's answer:
const QTextCursor old_cursor = widget.textCursor();
const int old_scrollbar_value = widget.verticalScrollBar()->value();
const bool is_scrolled_down = old_scrollbar_value == widget.verticalScrollBar()->maximum();
// Move the cursor to the end of the document.
widget.moveCursor(QTextCursor::End);
// Insert the text at the position of the cursor (which is the end of the document).
widget.textCursor().insertText(text);
if (old_cursor.hasSelection() || !is_scrolled_down)
{
// The user has selected text or scrolled away from the bottom: maintain position.
widget.setTextCursor(old_cursor);
widget.verticalScrollBar()->setValue(old_scrollbar_value);
}
else
{
// The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom.
widget.moveCursor(QTextCursor::End);
widget.verticalScrollBar()->setValue(verticalScrollBar()->maximum());
}