From a72c30020bdadbe0d82e583e17acd25715604f7b Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 13 Jul 2009 21:43:12 +0200 Subject: Bad drawing of styled viewports within QAbstractScrollArea This patch includes lots of refactoring, but the real problem was that in QWidgetPrivate::paintBackground we call drawPrimitive(PE_Widget) with a potentialy translated painter, but the opt.rect is not translated. When having a scroll area the calling function used to translated the painter and then pass the offset around to rectify. but drawPrimitive cannot rectify it. The solution is not to translate the painter but use other way to rectify the brush Task-number: 257517 Reviewed-by: bnilsen --- src/gui/kernel/qcocoaview_mac.mm | 10 +--- src/gui/kernel/qwidget.cpp | 58 ++++++++++++---------- src/gui/kernel/qwidget_mac.mm | 11 +--- src/gui/kernel/qwidget_p.h | 2 +- src/gui/styles/qmacstyle_mac.mm | 11 ++-- .../tst_qabstractscrollarea.cpp | 35 +++++++++++++ 6 files changed, 75 insertions(+), 52 deletions(-) diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index 52685ca..3e5bfb6 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -550,15 +550,7 @@ extern "C" { qwidget->objectName().local8Bit().data()); #endif QPainter p(qwidget); - QAbstractScrollArea *scrollArea = qobject_cast(qwidget->parent()); - QPoint scrollAreaOffset; - if (scrollArea && scrollArea->viewport() == qwidget) { - QAbstractScrollAreaPrivate *priv - = static_cast(qt_widget_private(scrollArea)); - scrollAreaOffset = priv->contentsOffset(); - p.translate(-scrollAreaOffset); - } - qwidgetprivate->paintBackground(&p, qrgn, scrollAreaOffset, + qwidgetprivate->paintBackground(&p, qrgn, qwidget->isWindow() ? QWidgetPrivate::DrawAsRoot : 0); p.end(); } diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 5f076ff..7026525 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -1966,10 +1966,9 @@ void QPixmap::fill( const QWidget *widget, const QPoint &off ) QPainter p(this); p.translate(-off); widget->d_func()->paintBackground(&p, QRect(off, size())); - } -static inline void fillRegion(QPainter *painter, const QRegion &rgn, const QPoint &offset, const QBrush &brush) +static inline void fillRegion(QPainter *painter, const QRegion &rgn, const QBrush &brush) { Q_ASSERT(painter); @@ -1978,26 +1977,39 @@ static inline void fillRegion(QPainter *painter, const QRegion &rgn, const QPoin // Optimize pattern filling on mac by using HITheme directly // when filling with the standard widget background. // Defined in qmacstyle_mac.cpp - extern void qt_mac_fill_background(QPainter *painter, const QRegion &rgn, const QPoint &offset, const QBrush &brush); - qt_mac_fill_background(painter, rgn, offset, brush); + extern void qt_mac_fill_background(QPainter *painter, const QRegion &rgn, const QBrush &brush); + qt_mac_fill_background(painter, rgn, brush); #else - const QRegion translated = rgn.translated(offset); - const QRect rect(translated.boundingRect()); - painter->setClipRegion(translated); + const QRect rect(rgn.boundingRect()); + painter->setClipRegion(rgn); painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft()); #endif } else { const QVector &rects = rgn.rects(); for (int i = 0; i < rects.size(); ++i) - painter->fillRect(rects.at(i).translated(offset), brush); + painter->fillRect(rects.at(i), brush); } } - -void QWidgetPrivate::paintBackground(QPainter *painter, const QRegion &rgn, const QPoint &offset, int flags) const +void QWidgetPrivate::paintBackground(QPainter *painter, const QRegion &rgn, int flags) const { Q_Q(const QWidget); +#ifndef QT_NO_SCROLLAREA + bool resetBrushOrigin = false; + QPointF oldBrushOrigin; + //If we are painting the viewport of a scrollarea, we must apply an offset to the brush in case we are drawing a texture + QAbstractScrollArea *scrollArea = qobject_cast(parent); + if (scrollArea && scrollArea->viewport() == q) { + QObjectData *scrollPrivate = static_cast(scrollArea)->d_ptr; + QAbstractScrollAreaPrivate *priv = static_cast(scrollPrivate); + oldBrushOrigin = painter->brushOrigin(); + resetBrushOrigin = true; + painter->setBrushOrigin(-priv->contentsOffset()); + + } +#endif // QT_NO_SCROLLAREA + const QBrush autoFillBrush = q->palette().brush(q->backgroundRole()); if ((flags & DrawAsRoot) && !(q->autoFillBackground() && autoFillBrush.isOpaque())) { @@ -2006,18 +2018,24 @@ void QWidgetPrivate::paintBackground(QPainter *painter, const QRegion &rgn, cons if (!(flags & DontSetCompositionMode) && painter->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) painter->setCompositionMode(QPainter::CompositionMode_Source); //copy alpha straight in #endif - fillRegion(painter, rgn, offset, bg); + fillRegion(painter, rgn, bg); } if (q->autoFillBackground()) - fillRegion(painter, rgn, offset, autoFillBrush); + fillRegion(painter, rgn, autoFillBrush); + if (q->testAttribute(Qt::WA_StyledBackground)) { - painter->setClipRegion(rgn.translated(offset)); + painter->setClipRegion(rgn); QStyleOption opt; opt.initFrom(q); q->style()->drawPrimitive(QStyle::PE_Widget, &opt, painter, q); } + +#ifndef QT_NO_SCROLLAREA + if (resetBrushOrigin) + painter->setBrushOrigin(oldBrushOrigin); +#endif // QT_NO_SCROLLAREA } /* @@ -4998,19 +5016,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP && !q->testAttribute(Qt::WA_OpaquePaintEvent) && !q->testAttribute(Qt::WA_NoSystemBackground)) { QPainter p(q); - QPoint scrollAreaOffset; - -#ifndef QT_NO_SCROLLAREA - QAbstractScrollArea *scrollArea = qobject_cast(parent); - if (scrollArea && scrollArea->viewport() == q) { - QObjectData *scrollPrivate = static_cast(scrollArea)->d_ptr; - QAbstractScrollAreaPrivate *priv = static_cast(scrollPrivate); - scrollAreaOffset = priv->contentsOffset(); - p.translate(-scrollAreaOffset); - } -#endif // QT_NO_SCROLLAREA - - paintBackground(&p, toBePainted, scrollAreaOffset, (asRoot || onScreen) ? flags | DrawAsRoot : 0); + paintBackground(&p, toBePainted, (asRoot || onScreen) ? flags | DrawAsRoot : 0); } if (!sharedPainter) diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index 5577224..84c3def 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -1198,16 +1198,7 @@ OSStatus QWidgetPrivate::qt_widget_event(EventHandlerCallRef er, EventRef event, p.setClipping(false); if(was_unclipped) widget->setAttribute(Qt::WA_PaintUnclipped); - - QAbstractScrollArea *scrollArea = qobject_cast(widget->parent()); - QPoint scrollAreaOffset; - if (scrollArea && scrollArea->viewport() == widget) { - QAbstractScrollAreaPrivate *priv = static_cast(static_cast(scrollArea)->d_ptr); - scrollAreaOffset = priv->contentsOffset(); - p.translate(-scrollAreaOffset); - } - - widget->d_func()->paintBackground(&p, qrgn, scrollAreaOffset, widget->isWindow() ? DrawAsRoot : 0); + widget->d_func()->paintBackground(&p, qrgn, widget->isWindow() ? DrawAsRoot : 0); if (widget->testAttribute(Qt::WA_TintedBackground)) { QColor tint = widget->palette().window().color(); tint.setAlphaF(.6); diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index 626950e..998181e 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -295,7 +295,7 @@ public: void setUpdatesEnabled_helper(bool ); - void paintBackground(QPainter *, const QRegion &, const QPoint & = QPoint(), int flags = DrawAsRoot) const; + void paintBackground(QPainter *, const QRegion &, int flags = DrawAsRoot) const; bool isAboutToShow() const; QRegion prepareToRender(const QRegion ®ion, QWidget::RenderFlags renderFlags); void render_helper(QPainter *painter, const QPoint &targetOffset, const QRegion &sourceRegion, diff --git a/src/gui/styles/qmacstyle_mac.mm b/src/gui/styles/qmacstyle_mac.mm index c08009b..5d75392 100644 --- a/src/gui/styles/qmacstyle_mac.mm +++ b/src/gui/styles/qmacstyle_mac.mm @@ -1871,24 +1871,23 @@ QPixmap QMacStylePrivate::generateBackgroundPattern() const Fills the given \a rect with the pattern stored in \a brush. As an optimization, HIThemeSetFill us used directly if we are filling with the standard background. */ -void qt_mac_fill_background(QPainter *painter, const QRegion &rgn, const QPoint &offset, const QBrush &brush) +void qt_mac_fill_background(QPainter *painter, const QRegion &rgn, const QBrush &brush) { QPoint dummy; const QPaintDevice *target = painter->device(); const QPaintDevice *redirected = QPainter::redirected(target, &dummy); const bool usePainter = redirected && redirected != target; - const QRegion translated = rgn.translated(offset); if (!usePainter && qt_mac_backgroundPattern && qt_mac_backgroundPattern->cacheKey() == brush.texture().cacheKey()) { - painter->setClipRegion(translated); + painter->setClipRegion(rgn); CGContextRef cg = qt_mac_cg_context(target); CGContextSaveGState(cg); HIThemeSetFill(kThemeBrushDialogBackgroundActive, 0, cg, kHIThemeOrientationInverted); - const QVector &rects = translated.rects(); + const QVector &rects = rgn.rects(); for (int i = 0; i < rects.size(); ++i) { const QRect rect(rects.at(i)); // Anchor the pattern to the top so it stays put when the window is resized. @@ -1899,8 +1898,8 @@ void qt_mac_fill_background(QPainter *painter, const QRegion &rgn, const QPoint CGContextRestoreGState(cg); } else { - const QRect rect(translated.boundingRect()); - painter->setClipRegion(translated); + const QRect rect(rgn.boundingRect()); + painter->setClipRegion(rgn); painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft()); } } diff --git a/tests/auto/qabstractscrollarea/tst_qabstractscrollarea.cpp b/tests/auto/qabstractscrollarea/tst_qabstractscrollarea.cpp index e976c1b..556e850 100644 --- a/tests/auto/qabstractscrollarea/tst_qabstractscrollarea.cpp +++ b/tests/auto/qabstractscrollarea/tst_qabstractscrollarea.cpp @@ -66,6 +66,7 @@ private slots: void setScrollBars(); void setScrollBars2(); void objectNaming(); + void patternBackground(); void viewportCrash(); void task214488_layoutDirection_data(); @@ -350,5 +351,39 @@ void tst_QAbstractScrollArea::task214488_layoutDirection() QVERIFY(lessThan ? (hbar->value() < refValue) : (hbar->value() > refValue)); } +void tst_QAbstractScrollArea::patternBackground() +{ + QScrollArea scrollArea; + scrollArea.resize(200, 200); + QWidget widget; + widget.resize(600, 600); + scrollArea.setWidget(&widget); + scrollArea.show(); + + QLinearGradient linearGrad(QPointF(250, 250), QPointF(300, 300)); + linearGrad.setColorAt(0, Qt::yellow); + linearGrad.setColorAt(1, Qt::red); + QBrush bg(linearGrad); + scrollArea.viewport()->setPalette(QPalette(Qt::black, bg, bg, bg, bg, bg, bg, bg, bg)); + widget.setPalette(Qt::transparent); + + QTest::qWait(50); + + QImage image(200, 200, QImage::Format_ARGB32); + scrollArea.render(&image); + + QCOMPARE(image.pixel(QPoint(20,20)) , QColor(Qt::yellow).rgb()); + + QScrollBar *hbar = scrollArea.horizontalScrollBar(); + hbar->setValue(hbar->maximum()); + QScrollBar *vbar = scrollArea.verticalScrollBar(); + vbar->setValue(vbar->maximum()); + + QTest::qWait(50); + + scrollArea.render(&image); + QCOMPARE(image.pixel(QPoint(20,20)) , QColor(Qt::red).rgb()); +} + QTEST_MAIN(tst_QAbstractScrollArea) #include "tst_qabstractscrollarea.moc" -- cgit v0.12