Mac: refactor scrollbar animations
authorJ-P Nurmi <jpnurmi@digia.com>
Tue, 30 Oct 2012 12:47:20 +0000 (13:47 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 2 Nov 2012 18:49:22 +0000 (19:49 +0100)
Get rid of QWidget-centric QMacStyle::eventFilter() and implement the
fade out animations for scrollbars using QNumberStyleAnimation-based
QFadeOutAnimation.

Change-Id: I2000fa50d46b153e981ceafc12a53932a196382e
Reviewed-by: Jens Bache-Wiig <jens.bache-wiig@digia.com>
17 files changed:
src/widgets/styles/qcommonstyle.cpp
src/widgets/styles/qmacstyle_mac.h
src/widgets/styles/qmacstyle_mac.mm
src/widgets/styles/qmacstyle_mac_p.h
src/widgets/styles/qstyle.cpp
src/widgets/styles/qstyle.h
src/widgets/widgets/qabstractscrollarea.cpp
src/widgets/widgets/qabstractscrollarea.h
src/widgets/widgets/qabstractscrollarea_p.h
src/widgets/widgets/qscrollarea.cpp
src/widgets/widgets/qscrollbar.cpp
src/widgets/widgets/qscrollbar.h
src/widgets/widgets/qscrollbar_p.h [new file with mode: 0644]
src/widgets/widgets/widgets.pri
tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp
tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp
tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp

index 2b6d843..96a2078 100644 (file)
@@ -5016,6 +5016,9 @@ int QCommonStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget
     case SH_RequestSoftwareInputPanel:
         ret = RSIP_OnMouseClickAndAlreadyFocused;
         break;
+    case SH_ScrollBar_Transient:
+        ret = false;
+        break;
     default:
         ret = 0;
         break;
index 2e4d2ff..1de1dbb 100644 (file)
@@ -125,9 +125,6 @@ public:
                       Qt::Orientation orientation, const QStyleOption *option = 0,
                       const QWidget *widget = 0) const;
 
-protected:
-    bool eventFilter(QObject *, QEvent *);
-
 private:
     Q_DISABLE_COPY(QMacStyle)
     Q_DECLARE_PRIVATE(QMacStyle)
index 0da18c6..901208a 100644 (file)
@@ -127,9 +127,9 @@ QMacStylePrivate *mPrivate;
 {
     Q_UNUSED(notification);
     QEvent event(QEvent::StyleChange);
-    Q_FOREACH (const QObject* target, mPrivate->animationTargets()) {
-        if (target)
-            QCoreApplication::sendEvent(const_cast<QObject*>(target), &event);
+    foreach (QWidget *widget, QApplication::allWidgets()) {
+        if (QScrollBar *scrollBar = qobject_cast<QScrollBar *>(widget))
+            QCoreApplication::sendEvent(scrollBar, &event);
     }
 }
 @end
@@ -178,38 +178,6 @@ static bool isVerticalTabs(const QTabBar::Shape shape) {
                 || shape == QTabBar::TriangularWest);
 }
 
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
-/*!
- Returns the QAbstractScrollArea the scroll bar \a sb is in. If \a sb is not
- inside of a QAbstractScrollArea, this returns 0.
- \internal
- */
-static const QAbstractScrollArea *scrollBarsScrollArea(const QScrollBar *sb)
-{
-    const QWidget *w = sb;
-    const QAbstractScrollArea *sa = 0;
-    while (w != 0 && sa == 0) {
-        sa = qobject_cast<const QAbstractScrollArea *>(w);
-        w = w->parentWidget();
-    }
-    return sa;
-}
-
-/*!
- For a scroll bar \a sb within a scroll area, this function returns all other scroll
- bars within the same scroll area.
- \internal
- */
-static QList<const QScrollBar *> scrollBarsSiblings(const QScrollBar *sb)
-{
-    const QAbstractScrollArea *sa = scrollBarsScrollArea(sb);
-    Q_ASSERT(sa != 0);
-    QList<const QScrollBar *> list = sa->findChildren<const QScrollBar *>();
-    list.removeOne(sb);
-    return list;
-}
-#endif
-
 void drawTabCloseButton(QPainter *p, bool hover, bool active, bool selected)
 {
     // draw background circle
@@ -1678,24 +1646,6 @@ QMacStylePrivate::QMacStylePrivate()
 
 }
 
-bool QMacStylePrivate::addWidget(QWidget *w)
-{
-    //already knew of it
-    if (w == defaultButton || animation(w))
-        return false;
-
-    Q_Q(QMacStyle);
-    if (qobject_cast<QScrollBar *>(w)) {
-        w->installEventFilter(q);
-        return true;
-    }
-    if (w->isWindow()) {
-        w->installEventFilter(q);
-        return true;
-    }
-    return false;
-}
-
 ThemeDrawState QMacStylePrivate::getDrawState(QStyle::State flags)
 {
     ThemeDrawState tds = kThemeStateActive;
@@ -1713,73 +1663,6 @@ ThemeDrawState QMacStylePrivate::getDrawState(QStyle::State flags)
     return tds;
 }
 
-/*! \reimp */
-bool QMacStyle::eventFilter(QObject *o, QEvent *e)
-{
-    //animate
-    Q_D(QMacStyle);
-    if (QScrollBar *sb = qobject_cast<QScrollBar *>(o)) {
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
-        // take care of fading out overlaying scrollbars (and only those!) when inactive
-        const QAbstractScrollArea *scrollArea = scrollBarsScrollArea(sb);
-        if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7 &&
-                [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay && scrollArea) {
-            QMacStylePrivate::OverlayScrollBarInfo& info = d->scrollBarInfos[sb];
-            const qint64 dt = QDateTime::currentMSecsSinceEpoch();
-            const qreal elapsed = dt - info.lastUpdate;
-            const CGFloat opacity = 1.0 - qMax(0.0, (elapsed - QMacStylePrivate::ScrollBarFadeOutDelay)
-                                                          / QMacStylePrivate::ScrollBarFadeOutDuration);
-            switch (e->type()) {
-            case QEvent::MouseMove:
-                // whenever the mouse moves on a not 100% transparent scroll bar,
-                // the fade out is stopped and it's set to 100% opaque
-                if (opacity > 0.0) {
-                    info.hovered = true;
-                    info.lastUpdate = dt;
-                    info.lastHovered = info.lastUpdate;
-                    sb->update();
-                    break;
-                }
-
-                // fall through
-            case QEvent::MouseButtonPress:
-            case QEvent::MouseButtonRelease:
-            case QEvent::MouseButtonDblClick:
-                // all mouse events which happens on a transparent scroll bar are
-                // translated and passed to the scroll area's viewport
-                if (opacity <= 0.0) {
-                    QMouseEvent* mouse = static_cast<QMouseEvent *>(e);
-                    QWidget* viewport = scrollArea->viewport();
-                    const QPoint scrollAreaPos = sb->mapTo(scrollArea, mouse->pos());
-                    const QPoint viewportPos = viewport->mapFromParent(scrollAreaPos);
-                    QMouseEvent me(mouse->type(), viewportPos, mouse->windowPos(),
-                                   mouse->globalPos(), mouse->button(), mouse->buttons(),
-                                   mouse->modifiers());
-                    QCoreApplication::sendEvent(viewport, &me);
-                    mouse->setAccepted(me.isAccepted());
-                    return true;
-                }
-                break;
-            case QEvent::Leave:
-            case QEvent::WindowDeactivate:
-                // mouse leave and window deactivate sets the scrollbar to not-hovered
-                // -> triggers fade out
-                info.hovered = false;
-                break;
-                if (!info.hovered) {
-                    e->setAccepted(false);
-                    return true;
-                }
-                break;
-            default:
-                break;
-            }
-        }
-#endif
-    }
-    return false;
-}
-
 void QMacStylePrivate::drawColorlessButton(const HIRect &macRect, HIThemeButtonDrawInfo *bdi,
                                            QPainter *p, const QStyleOption *opt) const
 {
@@ -2006,8 +1889,6 @@ void QMacStyle::unpolish(QApplication *)
 
 void QMacStyle::polish(QWidget* w)
 {
-    Q_D(QMacStyle);
-    d->addWidget(w);
     if (qt_mac_is_metal(w) && !w->testAttribute(Qt::WA_SetPalette)) {
         // Set a clear brush so that the metal shines through.
         QPalette pal = w->palette();
@@ -2059,13 +1940,13 @@ void QMacStyle::polish(QWidget* w)
 
     if (qobject_cast<QScrollBar*>(w)) {
         w->setAttribute(Qt::WA_OpaquePaintEvent, false);
+        w->setAttribute(Qt::WA_Hover, true);
         w->setMouseTracking(true);
     }
 }
 
 void QMacStyle::unpolish(QWidget* w)
 {
-    Q_D(QMacStyle);
     if ((qobject_cast<QMenu*>(w) || qt_mac_is_metal(w)) && !w->testAttribute(Qt::WA_SetPalette)) {
         QPalette pal = qApp->palette(w);
         w->setPalette(pal);
@@ -2093,8 +1974,8 @@ void QMacStyle::unpolish(QWidget* w)
 
     if (qobject_cast<QScrollBar*>(w)) {
         w->setAttribute(Qt::WA_OpaquePaintEvent, true);
+        w->setAttribute(Qt::WA_Hover, false);
         w->setMouseTracking(false);
-        d->scrollBarInfos.remove(w);
     }
 }
 
@@ -2303,8 +2184,7 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW
     case PM_ScrollBarExtent: {
 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
         if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7 &&
-           [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay &&
-           scrollBarsScrollArea(qobject_cast<const QScrollBar *>(widget))) {
+           [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay) {
             switch (d->aquaSizeConstrain(opt, widget)) {
             case QAquaSizeUnknown:
             case QAquaSizeLarge:
@@ -2831,6 +2711,12 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w
     case SH_DockWidget_ButtonsHaveFrame:
         ret = false;
         break;
+    case SH_ScrollBar_Transient:
+        ret = QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7;
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+        ret &= [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay;
+#endif
+        break;
     default:
         ret = QCommonStyle::styleHint(sh, opt, w, hret);
         break;
@@ -5023,59 +4909,65 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex
             }
 
 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
-            const qint64 dt = QDateTime::currentMSecsSinceEpoch();
-            if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7 &&
-                [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay &&
-                scrollBarsScrollArea(qobject_cast<const QScrollBar *>(widget)) &&
-                cc == CC_ScrollBar) {
-                QMacStylePrivate::OverlayScrollBarInfo& info = d->scrollBarInfos[widget];
-                bool showSiblings = false;
-                if (info.lastValue != slider->sliderPosition ||
-                        info.lastMinimum != slider->minimum ||
-                        info.lastMaximum != slider->maximum ||
-                        info.lastSize != slider->rect.size()) {
-                    info.lastValue = slider->sliderPosition;
-                    info.lastMinimum = slider->minimum;
-                    info.lastSize = slider->rect.size();
-                    info.lastMaximum = slider->maximum;
-                    info.lastUpdate = dt;
-                    showSiblings = true;
-                }
-
-                const QList<const QScrollBar *> siblings =
-                    scrollBarsSiblings(qobject_cast<const QScrollBar *>(widget));
-                // keep last update (last change of value) time of all siblings in sync
-                Q_FOREACH (const QScrollBar *sibling, siblings) {
-                    info.lastUpdate = qMax(info.lastUpdate,
-                                           d->scrollBarInfos.value(sibling).lastUpdate);
-                    info.cleared = false;
-                    if (d->scrollBarInfos.value(sibling).hovered)
-                        info.lastUpdate = dt;
-                }
-
-                qreal elapsed = dt - info.lastHovered;
-                CGFloat opacity = 1.0 - qMax(0.0,
-                    (elapsed - QMacStylePrivate::ScrollBarFadeOutDelay) /
-                    QMacStylePrivate::ScrollBarFadeOutDuration);
-                const bool isHorizontal = slider->orientation == Qt::Horizontal;
-
-                if (info.hovered) {
-                    info.lastHovered = dt;
-                    info.lastUpdate = dt;
+            if (cc == CC_ScrollBar && proxy()->styleHint(SH_ScrollBar_Transient)) {
+                QObject *styleObject = opt->styleObject;
+                int oldPos = styleObject->property("_q_stylepos").toInt();
+                int oldMin = styleObject->property("_q_stylemin").toInt();
+                int oldMax = styleObject->property("_q_stylemax").toInt();
+                QRect oldRect = styleObject->property("_q_stylerect").toRect();
+                int oldState = styleObject->property("_q_stylestate").toInt();
+                uint oldActiveControls = styleObject->property("_q_stylecontrols").toUInt();
+
+                // a scrollbar is transient when the the scrollbar itself and
+                // its sibling are both inactive (ie. not pressed/hovered/moved)
+                bool transient = !opt->activeSubControls && !(slider->state & State_On);
+
+                CGFloat opacity = 0.0;
+                if (!transient ||
+                        oldPos != slider->sliderPosition ||
+                        oldMin != slider->minimum ||
+                        oldMax != slider->maximum ||
+                        oldRect != slider->rect ||
+                        oldState != slider->state ||
+                        oldActiveControls != slider->activeSubControls) {
+
+                    // if the scrollbar is transient or its attributes, geometry or
+                    // state has changed, the opacity is reset back to 100% opaque
                     opacity = 1.0;
-                    // if the current scroll bar is hovered, none of the others might fade out
-                    Q_FOREACH (const QScrollBar *sibling, siblings) {
-                        d->scrollBarInfos[sibling].lastUpdate = info.lastUpdate;
+
+                    styleObject->setProperty("_q_stylepos", slider->sliderPosition);
+                    styleObject->setProperty("_q_stylemin", slider->minimum);
+                    styleObject->setProperty("_q_stylemax", slider->maximum);
+                    styleObject->setProperty("_q_stylerect", slider->rect);
+                    styleObject->setProperty("_q_stylestate", static_cast<int>(slider->state));
+                    styleObject->setProperty("_q_stylecontrols", static_cast<uint>(slider->activeSubControls));
+
+                    if (transient) {
+                        QFadeOutAnimation *anim  = qobject_cast<QFadeOutAnimation *>(d->animation(styleObject));
+                        if (!anim) {
+                            anim = new QFadeOutAnimation(styleObject);
+                            d->startAnimation(anim);
+                        } else {
+                            // the scrollbar was already fading out while the
+                            // state changed -> restart the fade out animation
+                            anim->setCurrentTime(0);
+                        }
+                    } else {
+                        d->stopAnimation(styleObject);
                     }
                 }
 
-                // when one scroll bar was changed, all its siblings need a redraw as well, since
-                // either both scroll bars within a scroll area shall be visible or none
-                if (showSiblings) {
-                    Q_FOREACH (const QScrollBar *sibling, siblings)
-                        const_cast<QScrollBar *>(sibling)->update();
+                QFadeOutAnimation *anim = qobject_cast<QFadeOutAnimation *>(d->animation(styleObject));
+                if (anim) {
+                    // once a scrollbar was active (hovered/pressed), it retains
+                    // the active look even if it's no longer active while fading out
+                    if (oldActiveControls)
+                        anim->setActive(true);
+                    opacity = anim->currentValue();
                 }
 
+                const bool isHorizontal = slider->orientation == Qt::Horizontal;
+
                 CGContextSaveGState(cg);
                 [NSGraphicsContext saveGraphicsState];
 
@@ -5085,7 +4977,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex
                 [scroller initWithFrame:NSMakeRect(0, 0, slider->rect.width(), slider->rect.height())];
                 // mac os behaviour: as soon as one color channel is >= 128,
                 // the bg is considered bright, scroller is dark
-                const QColor bgColor = widget->palette().color(QPalette::Base);
+                const QColor bgColor = opt->palette.color(QPalette::Base);
                 const bool isDarkBg = bgColor.red() < 128 && bgColor.green() < 128 &&
                                       bgColor.blue() < 128;
                 if (isDarkBg)
@@ -5102,7 +4994,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex
                 [scroller setScrollerStyle:NSScrollerStyleOverlay];
 
                 // first we draw only the track, by using a disabled scroller
-                if (opacity > 0.0) {
+                if (opt->activeSubControls || (anim && anim->wasActive())) {
                     CGContextBeginTransparencyLayerWithRect(cg, qt_hirectForQRect(slider->rect),
                                                             NULL);
                     CGContextSetAlpha(cg, opacity);
@@ -5115,21 +5007,6 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex
                     CGContextEndTransparencyLayer(cg);
                 }
 
-                // afterwards we draw the knob, since we cannot drow the know w/o the track,
-                // we simulate a scrollbar with a knob from 0.0 to 1.0
-                elapsed = dt - info.lastUpdate;
-                opacity = 1.0 - qMax(0.0, (elapsed - QMacStylePrivate::ScrollBarFadeOutDelay) /
-                                          QMacStylePrivate::ScrollBarFadeOutDuration);
-                info.cleared = opacity <= 0.0;
-
-                if (info.animating && info.cleared) {
-                    d->stopAnimation(slider->styleObject);
-                    info.animating = false;
-                } else if (!info.animating && !info.cleared) {
-                    d->startAnimation(new QStyleAnimation(slider->styleObject));
-                    info.animating = true;
-                }
-
                 CGContextBeginTransparencyLayerWithRect(cg, qt_hirectForQRect(slider->rect), NULL);
                 CGContextSetAlpha(cg, opacity);
 
@@ -5170,6 +5047,8 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex
             } else
 #endif
             {
+                d->stopAnimation(opt->styleObject);
+
                 HIThemeDrawTrack(&tdi, tracking ? 0 : &macRect, cg,
                                  kHIThemeOrientationNormal);
                 if (cc == CC_Slider && slider->subControls & SC_SliderTickmarks) {
index af1c88d..f01d1e1 100644 (file)
@@ -158,9 +158,6 @@ public:
     static const qreal ScrollBarFadeOutDuration;
     static const qreal ScrollBarFadeOutDelay;
 
-    // Stuff from QAquaAnimate:
-    bool addWidget(QWidget *);
-
     enum Animates { AquaPushButton, AquaProgressBar, AquaListViewItemOpen, AquaScrollBar };
     static ThemeDrawState getDrawState(QStyle::State flags);
     QAquaWidgetSize aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
@@ -201,29 +198,6 @@ public:
     mutable QPointer<QObject> defaultButton;
     mutable QPointer<QObject> autoDefaultButton;
 
-    struct OverlayScrollBarInfo {
-        OverlayScrollBarInfo()
-            : lastValue(-1),
-              lastMinimum(-1),
-              lastMaximum(-1),
-              lastUpdate(QDateTime::currentMSecsSinceEpoch()),
-              hovered(false),
-              lastHovered(0),
-              cleared(false),
-              animating(false)
-        {}
-        int lastValue;
-        int lastMinimum;
-        int lastMaximum;
-        QSize lastSize;
-        qint64 lastUpdate;
-        bool hovered;
-        qint64 lastHovered;
-        bool cleared;
-        bool animating;
-    };
-    mutable QMap<const QWidget*, OverlayScrollBarInfo> scrollBarInfos;
-
     struct ButtonState {
         int frame;
         enum { ButtonDark, ButtonLight } dir;
@@ -237,6 +211,34 @@ public:
 #endif
 };
 
+class QFadeOutAnimation : public QNumberStyleAnimation
+{
+    Q_OBJECT
+
+public:
+    QFadeOutAnimation(QObject *target) : QNumberStyleAnimation(target), _active(false)
+    {
+        setDuration(QMacStylePrivate::ScrollBarFadeOutDelay + QMacStylePrivate::ScrollBarFadeOutDuration);
+        setDelay(QMacStylePrivate::ScrollBarFadeOutDelay);
+        setStartValue(1.0);
+        setEndValue(0.0);
+    }
+
+    bool wasActive() const { return _active; }
+    void setActive(bool active) { _active = active; }
+
+private slots:
+    void updateCurrentTime(int time)
+    {
+        QNumberStyleAnimation::updateCurrentTime(time);
+        if (qFuzzyIsNull(currentValue()))
+            target()->setProperty("visible", false);
+    }
+
+private:
+    bool _active;
+};
+
 QT_END_NAMESPACE
 
 #endif // QMACSTYLE_MAC_P_H
index f0d1d23..76d6efa 100644 (file)
@@ -1887,6 +1887,9 @@ void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment,
     \value SH_RequestSoftwareInputPanel Determines when a software input panel should
            be requested by input widgets. Returns an enum of type QStyle::RequestSoftwareInputPanel.
 
+    \value SH_ScrollBar_Transient Determines if the style supports transient scroll bars. Transient
+           scroll bars appear when the content is scrolled and disappear when they are no longer needed.
+
     \sa styleHint()
 */
 
index 48491b3..387645a 100644 (file)
@@ -698,6 +698,7 @@ public:
         SH_DockWidget_ButtonsHaveFrame,
         SH_ToolButtonStyle,
         SH_RequestSoftwareInputPanel,
+        SH_ScrollBar_Transient,
         // Add new style hint values here
 
         SH_CustomBase = 0xf0000000
index 8aa5534..3c21d76 100644 (file)
@@ -57,6 +57,7 @@
 #include <QDebug>
 
 #include "qabstractscrollarea_p.h"
+#include "qscrollbar_p.h"
 #include <qwidget.h>
 
 #include <private/qapplication_p.h>
@@ -183,6 +184,7 @@ QAbstractScrollAreaScrollBarContainer::QAbstractScrollAreaScrollBarContainer(Qt:
     layout->setMargin(0);
     layout->setSpacing(0);
     layout->addWidget(scrollBar);
+    layout->setSizeConstraint(QLayout::SetMaximumSize);
 }
 
 /*! \internal
@@ -266,6 +268,8 @@ void QAbstractScrollAreaPrivate::replaceScrollBar(QScrollBar *scrollBar,
     scrollBar->setSliderPosition(oldBar->sliderPosition());
     scrollBar->setTracking(oldBar->hasTracking());
     scrollBar->setValue(oldBar->value());
+    scrollBar->installEventFilter(q);
+    oldBar->removeEventFilter(q);
     delete oldBar;
 
     QObject::connect(scrollBar, SIGNAL(valueChanged(int)),
@@ -286,6 +290,7 @@ void QAbstractScrollAreaPrivate::init()
     hbar = scrollBarContainers[Qt::Horizontal]->scrollBar;
     hbar->setRange(0,0);
     scrollBarContainers[Qt::Horizontal]->setVisible(false);
+    hbar->installEventFilter(q);
     QObject::connect(hbar, SIGNAL(valueChanged(int)), q, SLOT(_q_hslide(int)));
     QObject::connect(hbar, SIGNAL(rangeChanged(int,int)), q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection);
     scrollBarContainers[Qt::Vertical] = new QAbstractScrollAreaScrollBarContainer(Qt::Vertical, q);
@@ -293,6 +298,7 @@ void QAbstractScrollAreaPrivate::init()
     vbar = scrollBarContainers[Qt::Vertical]->scrollBar;
     vbar->setRange(0,0);
     scrollBarContainers[Qt::Vertical]->setVisible(false);
+    vbar->installEventFilter(q);
     QObject::connect(vbar, SIGNAL(valueChanged(int)), q, SLOT(_q_vslide(int)));
     QObject::connect(vbar, SIGNAL(rangeChanged(int,int)), q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection);
     viewportFilter.reset(new QAbstractScrollAreaFilter(this));
@@ -323,10 +329,10 @@ void QAbstractScrollAreaPrivate::layoutChildren()
 {
     Q_Q(QAbstractScrollArea);
     bool needh = (hbarpolicy == Qt::ScrollBarAlwaysOn
-                  || (hbarpolicy == Qt::ScrollBarAsNeeded && hbar->minimum() < hbar->maximum()));
+                  || (hbarpolicy == Qt::ScrollBarAsNeeded && hbar->minimum() < hbar->maximum() && !hbar->sizeHint().isEmpty()));
 
     bool needv = (vbarpolicy == Qt::ScrollBarAlwaysOn
-                  || (vbarpolicy == Qt::ScrollBarAsNeeded && vbar->minimum() < vbar->maximum()));
+                  || (vbarpolicy == Qt::ScrollBarAsNeeded && vbar->minimum() < vbar->maximum() && !vbar->sizeHint().isEmpty()));
 
     QStyleOption opt(0);
     opt.init(q);
@@ -648,6 +654,7 @@ void QAbstractScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)
         d->layoutChildren();
     if (oldPolicy != d->vbarpolicy)
         d->scrollBarPolicyChanged(Qt::Vertical, d->vbarpolicy);
+    d->setScrollBarTransient(d->vbar, policy == Qt::ScrollBarAsNeeded);
 }
 
 
@@ -709,6 +716,7 @@ void QAbstractScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy polic
         d->layoutChildren();
     if (oldPolicy != d->hbarpolicy)
         d->scrollBarPolicyChanged(Qt::Horizontal, d->hbarpolicy);
+    d->setScrollBarTransient(d->hbar, policy == Qt::ScrollBarAsNeeded);
 }
 
 /*!
@@ -921,6 +929,20 @@ void QAbstractScrollArea::setViewportMargins(const QMargins &margins)
                        margins.right(), margins.bottom());
 }
 
+/*! \internal */
+bool QAbstractScrollArea::eventFilter(QObject *o, QEvent *e)
+{
+    Q_D(QAbstractScrollArea);
+    if ((o == d->hbar || o == d->vbar) && (e->type() == QEvent::HoverEnter || e->type() == QEvent::HoverLeave)) {
+        Qt::ScrollBarPolicy policy = o == d->hbar ? d->vbarpolicy : d->hbarpolicy;
+        if (policy == Qt::ScrollBarAsNeeded) {
+            QScrollBar *sibling = o == d->hbar ? d->vbar : d->hbar;
+            d->setScrollBarTransient(sibling, e->type() == QEvent::HoverLeave);
+        }
+    }
+    return QFrame::eventFilter(o, e);
+}
+
 /*!
     \fn bool QAbstractScrollArea::event(QEvent *event)
 
@@ -1421,12 +1443,26 @@ bool QAbstractScrollAreaPrivate::canStartScrollingAt( const QPoint &startPos )
     return true;
 }
 
+void QAbstractScrollAreaPrivate::flashScrollBars()
+{
+    if (hbarpolicy == Qt::ScrollBarAsNeeded)
+        hbar->d_func()->flash();
+    if (vbarpolicy == Qt::ScrollBarAsNeeded)
+        vbar->d_func()->flash();
+}
+
+void QAbstractScrollAreaPrivate::setScrollBarTransient(QScrollBar *scrollBar, bool transient)
+{
+    scrollBar->d_func()->setTransient(transient);
+}
+
 void QAbstractScrollAreaPrivate::_q_hslide(int x)
 {
     Q_Q(QAbstractScrollArea);
     int dx = xoffset - x;
     xoffset = x;
     q->scrollContentsBy(dx, 0);
+    flashScrollBars();
 }
 
 void QAbstractScrollAreaPrivate::_q_vslide(int y)
@@ -1435,6 +1471,7 @@ void QAbstractScrollAreaPrivate::_q_vslide(int y)
     int dy = yoffset - y;
     yoffset = y;
     q->scrollContentsBy(0, dy);
+    flashScrollBars();
 }
 
 void QAbstractScrollAreaPrivate::_q_showOrHideScrollBars()
index 560df9d..5ac1402 100644 (file)
@@ -96,6 +96,7 @@ protected:
     void setViewportMargins(int left, int top, int right, int bottom);
     void setViewportMargins(const QMargins &margins);
 
+    bool eventFilter(QObject *, QEvent *);
     bool event(QEvent *);
     virtual bool viewportEvent(QEvent *);
 
index d77d97e..7e2ca74 100644 (file)
@@ -92,6 +92,9 @@ public:
     virtual void scrollBarPolicyChanged(Qt::Orientation, Qt::ScrollBarPolicy) {}
     bool canStartScrollingAt( const QPoint &startPos );
 
+    void flashScrollBars();
+    void setScrollBarTransient(QScrollBar *scrollBar, bool transient);
+
     void _q_hslide(int);
     void _q_vslide(int);
     void _q_showOrHideScrollBars();
index be7ce10..576f77a 100644 (file)
@@ -331,7 +331,7 @@ bool QScrollArea::eventFilter(QObject *o, QEvent *e)
     if (o == d->widget && e->type() == QEvent::Resize)
         d->updateScrollBars();
 
-    return false;
+    return QAbstractScrollArea::eventFilter(o, e);
 }
 
 /*!
index aa45b4f..199aaf9 100644 (file)
@@ -55,7 +55,7 @@
 #include "qaccessible.h"
 #endif
 #include <limits.h>
-#include "qabstractslider_p.h"
+#include "qscrollbar_p.h"
 
 QT_BEGIN_NAMESPACE
 
@@ -201,26 +201,6 @@ QT_BEGIN_NAMESPACE
     \sa QScrollArea, QSlider, QDial, QSpinBox, {fowler}{GUI Design Handbook: Scroll Bar}, {Sliders Example}
 */
 
-class QScrollBarPrivate : public QAbstractSliderPrivate
-{
-    Q_DECLARE_PUBLIC(QScrollBar)
-public:
-    QStyle::SubControl pressedControl;
-    bool pointerOutsidePressedControl;
-
-    int clickOffset, snapBackPosition;
-
-    void activateControl(uint control, int threshold = 500);
-    void stopRepeatAction();
-    int pixelPosToRangeValue(int pos) const;
-    void init();
-    bool updateHoverControl(const QPoint &pos);
-    QStyle::SubControl newHoverControl(const QPoint &pos);
-
-    QStyle::SubControl hoverControl;
-    QRect hoverRect;
-};
-
 bool QScrollBarPrivate::updateHoverControl(const QPoint &pos)
 {
     Q_Q(QScrollBar);
@@ -249,6 +229,29 @@ QStyle::SubControl QScrollBarPrivate::newHoverControl(const QPoint &pos)
     return hoverControl;
 }
 
+void QScrollBarPrivate::setTransient(bool value)
+{
+    Q_Q(QScrollBar);
+    if (transient != value) {
+        transient = value;
+        if (transient) {
+            if (q->isVisible() && q->style()->styleHint(QStyle::SH_ScrollBar_Transient))
+                q->update();
+        } else if (!q->isVisible()) {
+            q->show();
+        }
+    }
+}
+
+void QScrollBarPrivate::flash()
+{
+    Q_Q(QScrollBar);
+    if (!flashed && q->style()->styleHint(QStyle::SH_ScrollBar_Transient)) {
+        flashed = true;
+        q->show();
+    }
+}
+
 void QScrollBarPrivate::activateControl(uint control, int threshold)
 {
     QAbstractSlider::SliderAction action = QAbstractSlider::SliderNoAction;
@@ -322,6 +325,8 @@ void QScrollBar::initStyleOption(QStyleOptionSlider *option) const
     option->upsideDown = d->invertedAppearance;
     if (d->orientation == Qt::Horizontal)
         option->state |= QStyle::State_Horizontal;
+    if (d->flashed || !d->transient)
+        option->state |= QStyle::State_On;
 }
 
 
@@ -379,6 +384,8 @@ void QScrollBarPrivate::init()
     invertedControls = true;
     pressedControl = hoverControl = QStyle::SC_None;
     pointerOutsidePressedControl = false;
+    transient = q->style()->styleHint(QStyle::SH_ScrollBar_Transient);
+    flashed = false;
     q->setFocusPolicy(Qt::NoFocus);
     QSizePolicy sp(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::Slider);
     if (orientation == Qt::Vertical)
@@ -476,6 +483,9 @@ bool QScrollBar::event(QEvent *event)
     if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
         d_func()->updateHoverControl(he->pos());
         break;
+    case QEvent::StyleChange:
+        d_func()->setTransient(style()->styleHint(QStyle::SH_ScrollBar_Transient));
+        break;
     default:
         break;
     }
@@ -521,6 +531,10 @@ void QScrollBar::paintEvent(QPaintEvent *)
         opt.activeSubControls = (QStyle::SubControl)d->hoverControl;
     }
     style()->drawComplexControl(QStyle::CC_ScrollBar, &opt, &p, this);
+    if (d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient)) {
+        d->flashed = false;
+        update();
+    }
 }
 
 /*!
index 4886361..3e9ac8a 100644 (file)
@@ -83,6 +83,7 @@ protected:
 
 
 private:
+    friend class QAbstractScrollAreaPrivate;
     friend Q_WIDGETS_EXPORT QStyleOptionSlider qt_qscrollbarStyleOption(QScrollBar *scrollBar);
 
     Q_DISABLE_COPY(QScrollBar)
diff --git a/src/widgets/widgets/qscrollbar_p.h b/src/widgets/widgets/qscrollbar_p.h
new file mode 100644 (file)
index 0000000..fb4f5b0
--- /dev/null
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCROLLBAR_P_H
+#define QSCROLLBAR_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qabstractslider_p.h"
+#include "qstyle.h"
+
+QT_BEGIN_NAMESPACE
+
+class QScrollBarPrivate : public QAbstractSliderPrivate
+{
+    Q_DECLARE_PUBLIC(QScrollBar)
+public:
+    QStyle::SubControl pressedControl;
+    bool pointerOutsidePressedControl;
+
+    int clickOffset, snapBackPosition;
+
+    void activateControl(uint control, int threshold = 500);
+    void stopRepeatAction();
+    int pixelPosToRangeValue(int pos) const;
+    void init();
+    bool updateHoverControl(const QPoint &pos);
+    QStyle::SubControl newHoverControl(const QPoint &pos);
+
+    QStyle::SubControl hoverControl;
+    QRect hoverRect;
+
+    bool transient;
+    void setTransient(bool value);
+
+    bool flashed;
+    void flash();
+};
+
+QT_END_NAMESPACE
+
+#endif // QSCROLLBAR_P_H
index c86bc1e..797f3e9 100644 (file)
@@ -46,6 +46,7 @@ HEADERS += \
         widgets/qradiobutton.h \
         widgets/qrubberband.h \
         widgets/qscrollbar.h \
+        widgets/qscrollbar_p.h \
         widgets/qscrollarea_p.h \
         widgets/qsizegrip.h \
         widgets/qslider.h \
index 7e246e5..5aea329 100644 (file)
@@ -1585,12 +1585,15 @@ void tst_QListWidget::task217070_scrollbarsAdjusted()
     v.setUniformItemSizes(true);
     v.resize(160,100);
     QTest::qWait(50);
+    QScrollBar *hbar = v.horizontalScrollBar();
+    QScrollBar *vbar = v.verticalScrollBar();
+    QVERIFY(hbar && vbar);
     for(int f=150; f>90 ; f--) {
         v.resize(f,100);
         QTest::qWait(30);
-        QVERIFY(v.verticalScrollBar()->isVisible());
-        //the vertical scrollbar must not be visible.
-        QVERIFY(!v.horizontalScrollBar()->isVisible());
+        QVERIFY(vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || vbar->isVisible());
+        //the horizontal scrollbar must not be visible.
+        QVERIFY(!hbar->isVisible());
     }
 }
 
index 80d77aa..9038ae3 100644 (file)
@@ -2696,7 +2696,7 @@ void tst_QTableView::indexAt_data()
       << 0 << 0;   // expected
 
     QTest::newRow("no hidden, no span, scroll (5,0), at (20,20)")
-      << 10 << 10  // dim
+      << 20 << 20  // dim
       << 40 << 40  // size
       << -1 << -1  // hide
       << -1 << -1  // pos
@@ -2706,7 +2706,7 @@ void tst_QTableView::indexAt_data()
       << 0 << 5;   // expected
 
     QTest::newRow("no hidden, no span, scroll (0,5), at (20,20)")
-      << 10 << 10  // dim
+      << 20 << 20  // dim
       << 40 << 40  // size
       << -1 << -1  // hide
       << -1 << -1  // pos
@@ -2716,7 +2716,7 @@ void tst_QTableView::indexAt_data()
       << 5 << 0;   // expected
 
     QTest::newRow("no hidden, no span, scroll (5,5), at (20,20)")
-      << 10 << 10  // dim
+      << 20 << 20  // dim
       << 40 << 40  // size
       << -1 << -1  // hide
       << -1 << -1  // pos
index 6fa9c10..51a49f4 100644 (file)
@@ -2105,40 +2105,40 @@ void tst_QMdiArea::updateScrollBars()
 
     QScrollBar *hbar = mdiArea.horizontalScrollBar();
     QVERIFY(hbar);
-    QVERIFY(!hbar->isVisible());
+    QVERIFY(hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || !hbar->isVisible());
 
     QScrollBar *vbar = mdiArea.verticalScrollBar();
     QVERIFY(vbar);
-    QVERIFY(!vbar->isVisible());
+    QVERIFY(vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || !vbar->isVisible());
 
     // Move sub-window 2 away.
     subWindow2->move(10000, 10000);
     qApp->processEvents();
-    QVERIFY(hbar->isVisible());
-    QVERIFY(vbar->isVisible());
+    QVERIFY(hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || hbar->isVisible());
+    QVERIFY(vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || vbar->isVisible());
 
     for (int i = 0; i < 2; ++i) {
     // Maximize sub-window 1 and make sure we don't have any scroll bars.
     subWindow1->showMaximized();
     qApp->processEvents();
     QVERIFY(subWindow1->isMaximized());
-    QVERIFY(!hbar->isVisible());
-    QVERIFY(!vbar->isVisible());
+    QVERIFY(hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || !hbar->isVisible());
+    QVERIFY(vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || !vbar->isVisible());
 
     // We still shouldn't get any scroll bars.
     mdiArea.resize(mdiArea.size() - QSize(20, 20));
     QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
     qApp->processEvents();
     QVERIFY(subWindow1->isMaximized());
-    QVERIFY(!hbar->isVisible());
-    QVERIFY(!vbar->isVisible());
+    QVERIFY(hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || !hbar->isVisible());
+    QVERIFY(vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || !vbar->isVisible());
 
     // Restore sub-window 1 and make sure we have scroll bars again.
     subWindow1->showNormal();
     qApp->processEvents();
     QVERIFY(!subWindow1->isMaximized());
-    QVERIFY(hbar->isVisible());
-    QVERIFY(vbar->isVisible());
+    QVERIFY(hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || hbar->isVisible());
+    QVERIFY(vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || vbar->isVisible());
         if (i == 0) {
             // Now, do the same when the viewport is scrolled.
             hbar->setValue(1000);