From 3c2bfbff5f4c836de32628710ab7701b0db083f7 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 30 Oct 2012 13:47:20 +0100 Subject: Mac: refactor scrollbar animations 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 --- src/widgets/styles/qcommonstyle.cpp | 3 + src/widgets/styles/qmacstyle_mac.h | 3 - src/widgets/styles/qmacstyle_mac.mm | 259 ++++++--------------- src/widgets/styles/qmacstyle_mac_p.h | 54 ++--- src/widgets/styles/qstyle.cpp | 3 + src/widgets/styles/qstyle.h | 1 + src/widgets/widgets/qabstractscrollarea.cpp | 41 +++- src/widgets/widgets/qabstractscrollarea.h | 1 + src/widgets/widgets/qabstractscrollarea_p.h | 3 + src/widgets/widgets/qscrollarea.cpp | 2 +- src/widgets/widgets/qscrollbar.cpp | 56 +++-- src/widgets/widgets/qscrollbar.h | 1 + src/widgets/widgets/qscrollbar_p.h | 89 +++++++ src/widgets/widgets/widgets.pri | 1 + .../itemviews/qlistwidget/tst_qlistwidget.cpp | 9 +- .../itemviews/qtableview/tst_qtableview.cpp | 6 +- .../auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp | 20 +- 17 files changed, 293 insertions(+), 259 deletions(-) create mode 100644 src/widgets/widgets/qscrollbar_p.h diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 2b6d843723..96a2078905 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -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; diff --git a/src/widgets/styles/qmacstyle_mac.h b/src/widgets/styles/qmacstyle_mac.h index 2e4d2ffa4c..1de1dbbdd8 100644 --- a/src/widgets/styles/qmacstyle_mac.h +++ b/src/widgets/styles/qmacstyle_mac.h @@ -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) diff --git a/src/widgets/styles/qmacstyle_mac.mm b/src/widgets/styles/qmacstyle_mac.mm index 0da18c6f6a..901208a342 100644 --- a/src/widgets/styles/qmacstyle_mac.mm +++ b/src/widgets/styles/qmacstyle_mac.mm @@ -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(target), &event); + foreach (QWidget *widget, QApplication::allWidgets()) { + if (QScrollBar *scrollBar = qobject_cast(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(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 scrollBarsSiblings(const QScrollBar *sb) -{ - const QAbstractScrollArea *sa = scrollBarsScrollArea(sb); - Q_ASSERT(sa != 0); - QList list = sa->findChildren(); - 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(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(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(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(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(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(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(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(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 siblings = - scrollBarsSiblings(qobject_cast(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(slider->state)); + styleObject->setProperty("_q_stylecontrols", static_cast(slider->activeSubControls)); + + if (transient) { + QFadeOutAnimation *anim = qobject_cast(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(sibling)->update(); + QFadeOutAnimation *anim = qobject_cast(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) { diff --git a/src/widgets/styles/qmacstyle_mac_p.h b/src/widgets/styles/qmacstyle_mac_p.h index af1c88d50d..f01d1e14c5 100644 --- a/src/widgets/styles/qmacstyle_mac_p.h +++ b/src/widgets/styles/qmacstyle_mac_p.h @@ -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 defaultButton; mutable QPointer 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 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 diff --git a/src/widgets/styles/qstyle.cpp b/src/widgets/styles/qstyle.cpp index f0d1d2319c..76d6efadee 100644 --- a/src/widgets/styles/qstyle.cpp +++ b/src/widgets/styles/qstyle.cpp @@ -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() */ diff --git a/src/widgets/styles/qstyle.h b/src/widgets/styles/qstyle.h index 48491b3e3e..387645ae11 100644 --- a/src/widgets/styles/qstyle.h +++ b/src/widgets/styles/qstyle.h @@ -698,6 +698,7 @@ public: SH_DockWidget_ButtonsHaveFrame, SH_ToolButtonStyle, SH_RequestSoftwareInputPanel, + SH_ScrollBar_Transient, // Add new style hint values here SH_CustomBase = 0xf0000000 diff --git a/src/widgets/widgets/qabstractscrollarea.cpp b/src/widgets/widgets/qabstractscrollarea.cpp index 8aa5534366..3c21d767be 100644 --- a/src/widgets/widgets/qabstractscrollarea.cpp +++ b/src/widgets/widgets/qabstractscrollarea.cpp @@ -57,6 +57,7 @@ #include #include "qabstractscrollarea_p.h" +#include "qscrollbar_p.h" #include #include @@ -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() diff --git a/src/widgets/widgets/qabstractscrollarea.h b/src/widgets/widgets/qabstractscrollarea.h index 560df9dcae..5ac140241e 100644 --- a/src/widgets/widgets/qabstractscrollarea.h +++ b/src/widgets/widgets/qabstractscrollarea.h @@ -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 *); diff --git a/src/widgets/widgets/qabstractscrollarea_p.h b/src/widgets/widgets/qabstractscrollarea_p.h index d77d97e03f..7e2ca741b1 100644 --- a/src/widgets/widgets/qabstractscrollarea_p.h +++ b/src/widgets/widgets/qabstractscrollarea_p.h @@ -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(); diff --git a/src/widgets/widgets/qscrollarea.cpp b/src/widgets/widgets/qscrollarea.cpp index be7ce10c8c..576f77a7b7 100644 --- a/src/widgets/widgets/qscrollarea.cpp +++ b/src/widgets/widgets/qscrollarea.cpp @@ -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); } /*! diff --git a/src/widgets/widgets/qscrollbar.cpp b/src/widgets/widgets/qscrollbar.cpp index aa45b4f54e..199aaf93de 100644 --- a/src/widgets/widgets/qscrollbar.cpp +++ b/src/widgets/widgets/qscrollbar.cpp @@ -55,7 +55,7 @@ #include "qaccessible.h" #endif #include -#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(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(); + } } /*! diff --git a/src/widgets/widgets/qscrollbar.h b/src/widgets/widgets/qscrollbar.h index 48863616ff..3e9ac8a47f 100644 --- a/src/widgets/widgets/qscrollbar.h +++ b/src/widgets/widgets/qscrollbar.h @@ -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 index 0000000000..fb4f5b07da --- /dev/null +++ b/src/widgets/widgets/qscrollbar_p.h @@ -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 diff --git a/src/widgets/widgets/widgets.pri b/src/widgets/widgets/widgets.pri index c86bc1eff7..797f3e9b76 100644 --- a/src/widgets/widgets/widgets.pri +++ b/src/widgets/widgets/widgets.pri @@ -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 \ diff --git a/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp b/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp index 7e246e5936..5aea32996c 100644 --- a/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp +++ b/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp @@ -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()); } } diff --git a/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp b/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp index 80d77aa652..9038ae3b00 100644 --- a/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp +++ b/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp @@ -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 diff --git a/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp b/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp index 6fa9c10ddf..51a49f467d 100644 --- a/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp +++ b/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp @@ -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); -- cgit v1.2.3