QTextDocument和选区绘画

QTextDocument and selection painting

我正在使用 QTextDocument::drawContents 在小部件上绘制一段文本。我目前的目标是拦截鼠标事件并模拟文本选择。如何处理鼠标已经很清楚了,但是显示结果让我很困惑。

就在我们开始之前:我不能使用 QLabel 让它自己处理选择(它不知道如何绘制不寻常的字符并弄乱行高(https://git.macaw.me/blue/squawk/issues/59)),我也不能不要在那里使用 QTextBrowser - 这只是一个消息气泡,我不准备在那里牺牲性能。

围绕 QTextDocument 有一个非常丰富的框架,但我找不到任何方法让它为我认为已选中的某些文本片段的背景着色。找到了一种围绕文本制作框架的方法,找到了一种绘制下划线文本的方法,但看起来这个框架根本无法在文本后面绘制背景。

我试过这样做,看看我是否可以在某些 QTextFrame 下选择片段并设置它的样式:

QTextDocument* bodyRenderer = new QTextDocument();
bodyRenderer->setHtml("some text");
bodyRenderer->setTextWidth(50);
painter->setBackgroundMode(Qt::BGMode::OpaqueMode); //this at least makes it color background under all text
QTextFrameFormat format = bodyRenderer->rootFrame()->frameFormat();
format.setBackground(option.palette.brush(QPalette::Active, QPalette::Highlight));
bodyRenderer->rootFrame()->setFrameFormat(format);
bodyRenderer->drawContents(painter);

这也行不通:

QTextBlock b = bodyRenderer->begin();
QTextBlockFormat format = b.blockFormat();
format.setBackground(option.palette.brush(QPalette::Active, QPalette::Highlight));
format.setProperty(QTextFormat::BackgroundBrush, option.palette.brush(QPalette::Active, QPalette::Highlight));
QTextCursor cursor(bodyRenderer);
cursor.setBlockFormat(format);
b = bodyRenderer->begin();
while (b.isValid() > 0) {
    QTextLayout* lay = b.layout();
    QTextLayout::FormatRange range;
    range.format = b.charFormat();
    range.start = 0;
    range.length = 2;
    lay->draw(painter, option.rect.topLeft(), {range});
    b = b.next();
}

有什么方法可以让这个框架做一件简单的事情——在一些文本后面画一个选择背景?如果没有 - 有没有一种方法可以将光标位置取消投影到坐标转换中,比如我可以从 QAbstractTextDocumentLayout::hitTest 进行反向操作只是为了了解自己在何处绘制该选择矩形?

您可以使用 QTextCursor 更改 selected 文本的背景。您一次只需 select 一个字符即可保持格式。下面是蓝色高亮的例子(为了对比,文本的颜色用白色高亮显示):

void MainWindow::paintEvent(QPaintEvent *event) {
  QPainter painter(this);
  painter.fillRect(contentsRect(), QBrush(QColor("white")));
  QTextDocument document;
  document.setHtml(QString("Hello <font size='20'>world</font> with Qt!"));

  int selectionStart = 3;
  int selectionEnd = selectionStart + 10;
  QTextCursor cursor(&document);
  cursor.setPosition(selectionStart);
  while (cursor.position() < selectionEnd) {
    cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); // select one symbol
    QTextCharFormat selectFormat = cursor.charFormat();
    selectFormat.setBackground(Qt::blue);
    selectFormat.setForeground(Qt::white);
    cursor.setCharFormat(selectFormat);                                // set format for selection
    cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
  }

  document.drawContents(&painter, contentsRect());
  QMainWindow::paintEvent(event);
}