Details
-
Bug
-
Resolution: Out of scope
-
P2: Important
-
4.3.4
-
None
Description
When having a QTableWidget with a custom rendered widget in each cell (large cells, say 200 pixels high and 1200 pixels wide on average) and scrolling then there is a "fighting" between Windows and the Qt scrolling mechanism. The following is observed:
After vertical scrolling the widget sometimes appears to be redrawn 3 times:
1. Drawn at new position
2. Drawn at old position
3. Drawn at new position
This is due to the widget being drawn or copied three times during each scroll operation:
1. Drawn at new position – a screen buffer copy to the new position by a call to Win32 MoveWindow() called in QWidgetPrivate::setWSGeometry() in qwidget_win.cpp.
2. Drawn at old position – a blit from the Qt backing store in direct response to the WM_ERASEBKGND event sent by MoveWindow: QWidgetBackingStore::blitToScreen() called at line 3086 of qapplication_win.cpp.
3. Drawn at new position after Widget::paintEvent() is called.
The main bug is that QWidgetBackingStore::blitToScreen() is redrawing the widget at the old position instead of the newly scrolled position even after MoveWindow() has copied the pixels to the new position. This produces unnecessary glitchy scrolling. I expect QWidgetBackingStore:: blitToScreen() to always draw in the right position even in the middle of a scroll/move operation.
A lesser bug is that it's not clear why all three redraws are necessary during scrolling.
Even ignoring the glitching from Bug #1, notice that when pressing and holding the down arrow scroll bar button (bottom of scroll bar), the top widget is moved first, then the next etc. This means that the amount of damage is minimized and hence necessary redraw is minimized. However, when pressing and holding the up arrow scroll bar button the top widget is also moved first, which means that it obscures the next widget etc and the scrolling is very glitchy because the widgets are moved in the wrong order. In this case the bottom widget should be moved first and so on, to avoid damage the same way it is avoided for scrolling in the other direction.
The problem is the loop in QWidgetPrivate::scrollChildren() which always scans the child list in a single direction. The problem could be easily
fixed if scrolling in each direction iterated through the children in opposite directions and the container kept widget zorder in sync with top-to-bottom order.
Recommended fix: scroll one direction using zorder and the other direction using reverse zorder, so parent can manage zorder for best scrolling. Alternate fix: sort widgets top to bottom before executing moves.
In the general case these won't always work, but in cases like QTableWidget where the child widgets are guaranteed non-overlapping this will work well and will maximise copies and minimise re-paints.
Note that Qt 4.4 non-native widgets are not going to solve the above problems if the child widgets are native.