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:
but as soon as the column is smaller, I get a messy overflow:
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!
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();
}