Экспериментами занимаюсь в свободное время, когда хватает сил, поэтому продвигается по чайной ложке. Кое-какие результаты есть.
Вад, прицепи проектик, где можно опробовать
Да я, собственно, упражняюсь на абсолютно пустом проекте: одно окно с QTextBrowser, куда из текстового файла загружаю содержимое.
но заранее могу сказать - даже Qt-креаторская справка так же сбивается, думаю, что причины такие же.
вот это-то и удивляет больше всего - раньше как-то не задумывался, а ведь это в плане юзабилити полный швах.
как вариант - помнить, какая строка и какой по счёту знак были на текущем курсоре, а после ресайза эту строку помещать по вертикали в это же место
Так и пытаюсь делать. Пока получается до смешного противоположно ожиданиям: я думал, что позицию точно вычислить сложновато, а по факту нерешённой пока остаётся задача, как бы точно спозиционировать после ресайза.
В общем, на данный момент делаю так: вешаю свой слот на сигнал скролл-бара о смене позиции, и там делаю hitTest у layout-а, получая точную позицию курсора в начале самой верхней видимой в окне строки
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QScrollBar * scrollBar = ui->textBrowser->verticalScrollBar();
QObject::connect(scrollBar, SIGNAL(valueChanged(int)),
this, SLOT(onScrollValueChanged(int)));
}
void MainWindow::onScrollValueChanged( int value )
{
QPointF pt(0, value);
QTextDocument * doc = ui->textBrowser->document();
QTextCursor cursor = ui->textBrowser->cursorForPosition(QPoint(0, 0)); // берём позицию курсора для левой верхней точки viewport
int pos = cursor.position();
// сохраняю pos в поле объекта - проверено, оно указывает точно в нужную позицию
this->cursorPos = pos;
}
Можно, в принципе, и сам курсор сохранять, чтоб потом оставалось только установить его.
Сначала, пока с cursorForPosition варианта не увидел, пробовал ещё брать позицию как pos = doc->documentLayout()->hitTest(QPointF(0, value/*scroll pos*/), Qt::FuzzyHit) - в принципе, абсолютно тот же результат, и, может, даже одним и тем же путём получается - пока не разбирался, есть ли разница по скорости, визуально нет проблем.
С восстановлением позиции ситуация, честно говоря, пока дурацкая: я удивлён тем, что не наблюдаю в Qt никакого способа открутить прокрутку точно в точку, где находится курсор. Есть только мутный метод QTextEdit::ensureCursorVisible() - он лишь обещает, что виджет отскроллится как-то так, что текущая позиция курсора в кадр попадёт, но такое позиционирование весьма грубое. То есть, пока костыль выглядит так:
void MainWindow::resizeEvent ( QResizeEvent * event )
{
Q_UNUSED(event);
// изменяем и применяем курсор
QTextCursor cursor = ui->textBrowser->textCursor();
cursor.setPosition(cursorPos);
ui->textBrowser->setTextCursor(cursor);
// скролл:
ui->textBrowser->ensureCursorVisible();
}
Кроме того, ещё надо проверять, чтобы в процессе resize не сохранялся результат onScrollValueChanged, либо программно скроллить только по завершении resize - я этого пока не делаю, но при грубом позиционировании это делать надо: иначе цепочка resize-ов сопровождается цепочкой "грубых" scroll-ов, и немного вразнос позиция уходит.
Но это, полагаю, неокончательный вариант - бывают случаи, когда, скажем, первая видимая строка после ресайза оказывается в самом низу, почти скрытая. Пока есть на примете костыль с пошаговым скроллом на несколько шагов вверх-вниз от "первого приближения", полученного ensureCursorVisible, как вариант, тем же бинарным поиском, пока не попаду в искомую позицию достаточно точно (как оказалось, получить значение текущей позиции проще, чем в ней оказаться
)
Либо, как альтернативный (и извращённый) вариант, вставлять в начале каждой строки по html-якорю #<line_num> и делать QTextEdit::scrollToAnchor с номером строки.
Первый вариант лучше - можно сделать свой кастомный контрол, унаследовав от стандартного и допилив поведение.