Hi,
In modern desktop environments it's common to display a descriptive line of text
below the icon labels in list views and icon views. This text is usually drawn
with a different text color or font weight, and in KDE we implement this feature
by using custom item delegates.
With view items being styleable in Qt 4.4 we'd like to draw the background in
those delegates by using QStyle::drawPrimitive() and PE_PanelItemViewItem,
but as it turns out there are a number of difficulties with doing this.
The main one is that when QStyleOptionViewItem::showDecorationSelected is
false, drawPrimitive() will call subElementRect() with SE_ItemViewItemText to
obtain the rectangle where the selection should be drawn. But when there are
additional text items that subElementRect() is unaware of, this will result in
the
selection background being drawn in the wrong place.
A possible solution to this problem would be to make it the caller's
responsibility
to set the rect in the style options to the rectangle where the selection should
be drawn. Another is to add a decoration and a text rectangle to the style
options, to be filled in by the caller. This latter option may be necessary for
the Mac style.
Another problem is knowing which color to use for the text items in a custom
delegate when the item is hovered or selected or both. The delegate could
assume that the style will draw the background using the highlight color when
the item is selected, but that might not be the case, possibly resulting in
unreadable text. Windows Vista for example doesn't use the normal highlight
color for selected items in icon views and tree views. To make matters worse
it does use it in list views.
For this reason I think it would make sense to extend QPalette with a set of
additional color roles for highlighted item view items, and possibly a role for
the sub text item. It would also be helpful if there was a style hint that
indicated which color role should be used for the text in listviews.
I've attached a simple program that demonstrates some of these issues.
Regards,
Fredrik
#include <QApplication>
#include <QBoxLayout>
#include <QListView>
#include <QPainter>
#include <QStandardItem>
#include <QStandardItemModel>
#include <QStyledItemDelegate>
#include <QWidget>
class ItemDelegate : public QStyledItemDelegate
{
public:
enum ItemDataRole { SubTextRole = Qt::UserRole + 100 };
ItemDelegate(QObject *parent = 0) : QStyledItemDelegate(parent) {}
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
void paint(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
QSize ItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QIcon icon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
QString line1 = index.data(Qt::DisplayRole).toString();
QString line2 = index.data(SubTextRole).toString();
int textW = qMax(option.fontMetrics.width(line1), option.fontMetrics.width(line2));
QSize iconSize = icon.actualSize(option.decorationSize);
return QSize(qMax(textW, iconSize.width()) + 4,
iconSize.height() + 2 + option.fontMetrics.lineSpacing() * 2 + 4);
}
void ItemDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
p->save();
QString line1 = index.data(Qt::DisplayRole).toString();
QString line2 = index.data(SubTextRole).toString();
QStyleOptionViewItemV4 opt(option);
initStyleOption(&opt, index);
QStyle *style = opt.widget->style();
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, p, opt.widget);
if (option.state & QStyle::State_Selected)
p->setPen(QPen(option.palette.brush(QPalette::HighlightedText), 0));
QRect itemRect = option.rect.adjusted(2, 2, -2, -2);
QRect r = QStyle::alignedRect(opt.direction, Qt::AlignTop | Qt::AlignHCenter,
opt.decorationSize, itemRect);
opt.icon.paint(p, r);
int h = option.fontMetrics.lineSpacing() * 2;
QRect textRect(itemRect.left(), itemRect.bottom() - h, itemRect.width(), h);
p->drawText(textRect, Qt::AlignTop | Qt::AlignHCenter, line1);
QColor subTextColor = p->pen().color();
subTextColor.setAlphaF(.5);
textRect.adjust(0, option.fontMetrics.lineSpacing(), 0, 0);
p->setPen(subTextColor);
p->drawText(textRect, Qt::AlignTop | Qt::AlignHCenter, line2);
p->restore();
}
class MainWindow : public QWidget
{
public:
MainWindow();
~MainWindow() {}
};
MainWindow::MainWindow() : QWidget()
{
QListView *listview = new QListView(this);
listview->setViewMode(QListView::IconMode);
listview->setItemDelegate(new ItemDelegate(listview));
listview->setSelectionMode(QAbstractItemView::ExtendedSelection);
listview->setMovement(QListView::Free);
QPixmap pixmap(32, 32);
pixmap.fill(Qt::transparent);
QPainter p(&pixmap);
p.setRenderHint(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
p.setBrush(Qt::green);
p.drawEllipse(pixmap.rect());
p.end();
QList<QStandardItem*> list;
for (int i = 0; i < 10; i++) {
QStandardItem *item = new QStandardItem(QIcon(pixmap), QString("item %1").arg(i + 1));
item->setData("sub text", ItemDelegate::SubTextRole);
list.append(item);
}
QStandardItemModel *model = new QStandardItemModel(this);
model->appendColumn(list);
listview->setModel(model);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(listview);
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}