I am painting a block of text on a widget with QTextDocument::drawContents
. My current goal is to intercept mouse events and emulate text selection. It's pretty clear how to handle the mouse, but displaying the result puzzles me a lot.
Just before we start: I can not use QLabel and let it handle selection on it's own (it has no idea how to draw unusual characters and messes up line height (https://git.macaw.me/blue/squawk/issues/59)), nor I can not use QTextBrowser there - it's just a message bubble, I'm not ready to sacrifice performance there.
There is a very rich framework around QTextDocument, but I can not find any way to make it color the background of some fragment of text that I would consider selected. Found a way to make a frame around a text, found a way to draw under-over-lined text, but it looks like there is simply just no way this framework can draw a background behind text.
I have tried doing this, to see if I can take selected fragment under some QTextFrame
and set it's style:
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);
Nothing of this works too:
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();
}
Is there any way I can make this framework do a simple thing - draw a selection background behind some text? If not - is there a way I can unproject cursor position into coordinate translation, like can I do reverse operation from QAbstractTextDocumentLayout::hitTest
just to understand where to draw that selection rectangle myself?
CodePudding user response:
You can use QTextCursor to change the background of the selected text. You only need to select one character at a time to keep the formatting. Here is an example of highlighting in blue (the color of the text is highlighted in white for contrast):
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);
}