Just a quick post to share a fairly important piece of information I found out today with the rest of the internets - hopefully this will help if someone is searching to find out why the list in their Qt application is so slow.

Basically, the setup I have is as follows: A model -> providing images/text to a delegate, text laid out in sizeHint using QTextLayout and friends -> rendered in paint onto the view. This is a pretty standard setup, though the info in this post probably applies for other API like QListWidget::setItemWidget() too - I haven't checked.

Back to the code, in my delegate paint event, I had the following, also fairly standard code:

    QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter);
This is nothing special. It ensures that Qt draws my selection rect around items when they are selected and all the other standard style prettiness.

However - and here's the kicker - my size hint wasn't always 70 pixels tall. "Why the hell is this an issue" I hear you ask? Good question.

Let's run our application and scroll around a bit on Maemo. Wow, that's slow. Let's profile.

What's that? Why is such a huge amount of time being spent in QMaemo5Style? That's insane - all it is doing is drawing a border and selection rectangle!

Time to go source diving...

















          case PE_PanelItemViewItem: {
{snip}
int rowHeight = 70;
if (GtkWidget *gtkTreeView = d->gtkWidget("HildonPannableArea.GtkTreeView"))
d->gtk_widget_style_get(gtkTreeView, "row-height", &rowHeight, NULL);

if (option->rect.height() != rowHeight) {
QPixmap scalePix(option->rect.width(), rowHeight);
scalePix.fill(Qt::transparent);
QPainter scalePainter(&scalePix);
QGtkPainter gtkScalePainter(&scalePainter);
gtkScalePainter.setUsePixmapCache(false); // cached externally

// the sapwood engine won't scale the image, but instead tile it, which looks ridiculous
gtkScalePainter.paintFlatBox(gtkTreeView, detail, QRect(0, 0, option->rect.width(), rowHeight),
option->state & State_Selected ? GTK_STATE_SELECTED :
option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE,
GTK_SHADOW_NONE, gtkTreeView->style);

// don't just scale the whole pixmap - the bottom border line would look extremly ugly for big items
int dh = 8; // just an arbitrary value which looks good with the default Maemo styles
p->drawPixmap(cacheRect.topLeft(), scalePix, QRect(0, 0, scalePix.width(), dh));
p->drawPixmap(cacheRect.adjusted(0, dh, 0, -dh), scalePix, QRect(0, dh, scalePix.width(), scalePix.height() - 2 * dh));
p->drawPixmap(cacheRect.bottomLeft() - QPoint(0, dh), scalePix, QRect(0, scalePix.height() - dh, scalePix.width(), dh));
} else {
gtkCachedPainter.paintFlatBox(gtkTreeView, detail, cacheRect,
option->state & State_Selected ? GTK_STATE_SELECTED :
option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE,
GTK_SHADOW_NONE, gtkTreeView->style);
}
}
END_STYLE_PIXMAPCACHE
break;

Oh dear. It appears that whenever a cell's row height is not 70px, QMaemo5Style renders onto a pixmap and does all sorts of voodoo to scale it and paint it where required. That is *not* going to be fast.

However, if it is 70px (the UI standard), it's rendered direct from cache. Much better.

(Can it be made better? I don't know. I was in a rush when looking into this - and regardless, with PR1.2 probably round the corner, it's a bit late - at least for now)

The workaround, at least in my case, was this in my delegate sizeHint:


QSize s(imageSize.width() + qMax(nameRect.width(), textRect.width()) + 20,
qMax(height, 60));

+ // Maemo is very, very bad with nonstandard row sizes.
+ // It involves a lot of pixmap resizing and other horrors which is *really* noticably slow.
+ // To prevent this, we min-bound rows at 70px (style default)
+ if (QApplication::style()->inherits("QMaemo5Style")) {
+ if (s.height() < 70) {
+ s.setHeight(70); // MAEMO hack
+ }
+ }
+

With this done, it's back to silky smooth resizing. Hopefully this helps someone else in a similar predicament. :)

Thanks to harryF from Qt for his help diagnosing this issue.