Highlight matched substrings in QStyledItemDelegate



  • I'm using a custom QStyledItemDelegate and I'd like to highlight or mark with different font color specific words that match a regular expression. How should I implement the paint method to highlight these words?



  • 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.


  • Qt Champions 2016

    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.


  • Qt Champions 2016

    @panosk
    Hello,
    It might get pretty complex seeing that you've enabled word wrapping and gave up on QTextDocument. 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:

    1. Calculate the most words per line.
    2. Draw the words one by one (with appropriate style). Draw a word and then move your current position right.
    3. 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!


  • Qt Champions 2016

    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, probably QTextDocument 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, probably QTextDocument 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!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.