1
votes

I want to display two columns inside a QTableWidget showing the differences between two stings (calculated by some Levenshtein Distance algorithms before). The parts are stored inside the data of each QTableWidgetItem, as a QStringList. The first part has to be displayed black, the next red, and then alternating again black, red and so on.

For this purpose I implemented a QStyledItemDelegate with a custom paint() function that eventually calls a drawText() method:

void DifferencesDelegate::drawText(QPainter *painter,
                                   const QStyleOptionViewItem &option,
                                   const QModelIndex &index) const
{
    painter->save();

    const QPen defaultPen = painter->pen();

    QStyleOptionViewItem opt = option;
    initStyleOption(&opt, index);
    opt.text.clear();

    QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
    style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);

    opt.rect.moveRight(opt.rect.right() + 3);

    int color = 1;
    for (const QString &part : index.data(Qt::UserRole).toStringList()) {
        color++;
        color = color % 2;
        if (color) {
            painter->setPen(Qt::red);
        } else {
            painter->setPen(defaultPen);
        }

        style->drawItemText(painter, opt.rect, opt.displayAlignment, opt.palette, true, part);
        opt.rect.moveRight(opt.rect.right() + painter->fontMetrics().width(part));
    }

    painter->restore();
}

This results in the correct painting as long as the column's width is sufficient:

Column's width sufficient

but as soon as the column is smaller, I get a messy overflow:

Column too narrow

This is surely caused by opt.rect being applied for each part of the display, but not for the text as a whole thing.

Only problem is that I have no idea how to fix this ;-) Any help would be greatly appreciated! Thanks in advance!

1

1 Answers

0
votes

Quite unexpectedly, I managed to solve it ;-)

void DifferencesDelegate::drawText(QPainter *painter, const QStyleOptionViewItem &option,
                                const QModelIndex &index) const
{
    if (! index.data(Qt::UserRole).isValid()) {
        TableDelegate::drawText(painter, option, index);
        return;
    }

    painter->save();

    const QPen defaultPen = painter->pen();

    QStyleOptionViewItem opt = option;
    initStyleOption(&opt, index);
    opt.text.clear();

    QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
    style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);

    constexpr int offset = 3;
    const QString ellipsis = QStringLiteral("…");
    const int ellipsisWidth = painter->fontMetrics().width(ellipsis);
    const int rightBorder = opt.rect.left() + opt.rect.width() - offset;

    QRect drawRect;
    int color = 1;
    int partWidth;
    bool overflow = false;

    opt.rect.moveRight(opt.rect.right() + offset);

    const QStringList parts = index.data(Qt::UserRole).toStringList();
    const int partsCount = parts.count();
    for (int i = 0; i < partsCount; i++) {
        color++;
        color = color % 2;
        if (color) {
            painter->setPen(Qt::red);
        } else {
            painter->setPen(defaultPen);
        }

        partWidth = painter->fontMetrics().width(parts.at(i));

        drawRect = opt.rect;
        if (drawRect.left() + partWidth + (i == partsCount - 1 ? 0 : ellipsisWidth) > rightBorder) {
            drawRect.setWidth(rightBorder - drawRect.left() - ellipsisWidth);
            overflow = true;
        }

        style->drawItemText(painter, drawRect, opt.displayAlignment, opt.palette, true,
                            parts.at(i));

        if (overflow) {
            drawRect.setLeft(rightBorder - ellipsisWidth);
            drawRect.setWidth(ellipsisWidth);
            style->drawItemText(painter, drawRect, opt.displayAlignment, opt.palette, true,
                                ellipsis);
            break;
        }

        opt.rect.moveRight(opt.rect.right() + partWidth);
    }

    painter->restore();
}