Highlight matched substrings in QStyledItemDelegate
-
A gentle bump with a little more information.
It seems I have trouble setting correctly the rectangle of the matched strings in relation to the whole text. Some early experiments:
void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItemV4 optionV4 = option; initStyleOption(&optionV4, index); if (optionV4.state & QStyle::State_Enabled) { const QRect rect(...); //Here I'm drawing the whole text painter->drawText(rect,Qt::TextWordWrap,index.data(Qt::DisplayRole).toString()); /*QTextDocument doc(optionV4.text);*/ // This could probably be a better option... QRegularExpression reg(MYREG); QRegularExpressionMatchIterator it = reg.globalMatch(optionV4.text); while(it.hasNext()) { QRegularExpressionMatch match = it.next(); QString found = match.captured(1); painter->save(); QFontMetrics fmt(optionV4.font); QRect foundRect = fmt.boundingRect(found); QBrush brush = optionV4.palette.background(); brush.setColor(Qt::yellow); painter->fillRect(foundRect,brush); painter->restore(); }
Currently something like this will cover the text and won't highlight it, but I hope that gives an idea of what I want to achieve.
Basically, I want to mark some words in some way and treat them differently (kind of placeholders) when editing the cell text, so the formatting I'll do in the delegate has to be also available in the editor (QTextEdit). The underlying model is a subclassed QSqlRelationalTableModel so I'm not sure if it's possible to move this logic to the data() method of my model and return these strings with a different role other than Qt::DisplayRole. Ideas are of course welcome.
-
Hello,
It seems you almost got it. As I see it, you only need to move the rectangle-filling part before drawing the text and it should light up.Kind regards.
EDIT:
Do you wish the font to change color or it's background? If the former, then probably you'll need to draw the text in parts. -
@kshegunov
Hello,Thanks for the tips. Changing font color would be probably better. In any case, the problem is the wrong offsets of the boundingRect. The width and height are correct, but the x and y points are the same (0,-14) no matter where the substring is found, which is expected as it's mentioned in the documentation:
Returns the rectangle that is covered by ink if character ch were to be drawn at the origin of the coordinate system.
So, I probably need to use the translate() method to move the substring rectangle to the right position in relation to the enclosing option.rect but it seems I have exhausted all my brain cells and can't figure out the calculation...:)
Also, if you could give any general clues how I should draw the text in parts I would appreciate it.
-
@panosk
Hello,
It might get pretty complex seeing that you've enabled word wrapping and gave up onQTextDocument
. The calculation could be done like this (I'm speculating here, have not tried it):
Split your text into words (in an array), get the bounding rectangle for each word (in an array).
Then repeat until you've exhausted all words:- Calculate the most words per line.
- Draw the words one by one (with appropriate style). Draw a word and then move your current position right.
- Go to next line (move your current position down).
It's certainly is not ideal, but will probably work. One problem I see is that this way is a bit clumsy and inflexible, and probably
QTextDocument
and it's support classes will give you better performance/possibilities.Kind regards.
-
@kshegunov
Well, I haven't given up on QTextDocument, actually I have a comment in my code posting that this could probably be a better option. I agree that things will get too complicated and considering that the model/view could contain thousand of rows, performance could be an issue. I'll go the QTextDocument route.Thanks a lot for your feedback!
-
Something like this should be sufficient ...
const QRect rect(...); // The item rectangle qint32 newLineOffset = 0; QRect spaceSize = fmt.boundingRect(QStringLiteral(" ")); QPoint position; // This would be our current position QStringList words = index.data(Qt::DisplayRole).toString().split(QStringLiteral(" "), QString::SkipEmptyParts); // Words array for (qint32 i = 0, size = words.size(); i < size && position.y() < rect.height(); i++) { QRect wordSize = fmt.boundingRect(words[i]); if (position.x() + wordSize.width() > rect.width()) { // We go to a newline position.x() = 0; position.y() += newLineOffset; newLineOffset = 0; } // Draw the word (you could insert your own style here) painter->drawText(position, words[i]); // Move right + space size position.x() += wordSize.width() + spaceSize.width(); // Update line height newLineOffset = qMax(newLineOffset, qMax(wordSize.height(), spaceSize.height())); }
EDIT:
Right I saw that you wrote a post. Yes, probablyQTextDocument
will be the more flexible way. Do consider the code sample I give you here though.Kind regards.
-
@kshegunov said:
EDIT:
Right I saw that you wrote a post. Yes, probablyQTextDocument
will be the more flexible way. Do consider the code sample I give you here though.Kind regards.
Thank you very much for this, I will definitely try it!