Support for overlay-scrollbars on Mac

On Lion scroll bars are within the scroll area itself and are not being
shown as long as the user is not scrolling.

This patch draws the new scroll bars and makes them fade away. Further
it introduces a pixel metric checking for this behaviour. It's used by
QAbstractScrollArea to put the viewport to the correct place.

Task-number: QTBUG-21673

Change-Id: Id530265043549318ac420b392de6b8642deaa4c6
Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
Reviewed-by: Gabriel de Dietrich <gabriel.dietrich-de@nokia.com>
This commit is contained in:
Christoph Schleifenbaum 2012-04-11 20:12:48 +02:00 committed by Qt by Nokia
parent a0e617f669
commit 10c6f015f4
6 changed files with 449 additions and 40 deletions

View File

@ -4444,6 +4444,9 @@ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWid
case PM_ScrollView_ScrollBarSpacing:
ret = 2 * proxy()->pixelMetric(PM_DefaultFrameWidth, opt, widget);
break;
case PM_ScrollView_ScrollBarOverlap:
ret = 0;
break;
case PM_SubMenuOverlap:
ret = -proxy()->pixelMetric(QStyle::PM_MenuPanelWidth, opt, widget);
break;

View File

@ -82,6 +82,7 @@
#include <qpushbutton.h>
#include <qradiobutton.h>
#include <qrubberband.h>
#include <qscrollbar.h>
#include <qsizegrip.h>
#include <qspinbox.h>
#include <qsplitter.h>
@ -102,6 +103,35 @@
#include <private/qstylehelper_p.h>
#include <qpa/qplatformfontdatabase.h>
QT_USE_NAMESPACE
@interface NotificationReceiver : NSObject {
QMacStylePrivate *mPrivate;
}
- (id)initWithPrivate:(QMacStylePrivate *)priv;
- (void)scrollBarStyleDidChange:(NSNotification *)notification;
@end
@implementation NotificationReceiver
- (id)initWithPrivate:(QMacStylePrivate *)priv
{
self = [super init];
mPrivate = priv;
return self;
}
- (void)scrollBarStyleDidChange:(NSNotification *)notification
{
Q_UNUSED(notification);
QEvent event(QEvent::StyleChange);
Q_FOREACH (QPointer<QWidget> sb, mPrivate->scrollBars) {
if (sb)
QCoreApplication::sendEvent(sb, &event);
}
}
@end
QT_BEGIN_NAMESPACE
// The following constants are used for adjusting the size
@ -115,6 +145,8 @@ const int QMacStylePrivate::SmallButtonH = 30;
const int QMacStylePrivate::BevelButtonW = 50;
const int QMacStylePrivate::BevelButtonH = 22;
const int QMacStylePrivate::PushButtonContentPadding = 6;
const qreal QMacStylePrivate::ScrollBarFadeOutDuration = 200.0;
const qreal QMacStylePrivate::ScrollBarFadeOutDelay = 450.0;
// These colors specify the titlebar gradient colors on
// Leopard. Ideally we should get them from the system.
@ -144,6 +176,38 @@ 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
@ -1640,6 +1704,9 @@ bool QMacStylePrivate::animatable(QMacStylePrivate::Animates as, const QWidget *
} else if (as == AquaProgressBar) {
if (progressBars.contains((const_cast<QWidget *>(w))))
return true;
} else if (as == AquaScrollBar) {
if (scrollBars.contains((const_cast<QWidget *>(w))))
return true;
}
return false;
}
@ -1652,6 +1719,9 @@ void QMacStylePrivate::stopAnimate(QMacStylePrivate::Animates as, QWidget *w)
tmp->update();
} else if (as == AquaProgressBar) {
progressBars.removeAll(w);
} else if (as == AquaScrollBar) {
scrollBars.removeAll(w);
scrollBarInfos.remove(w);
}
}
@ -1661,12 +1731,14 @@ void QMacStylePrivate::startAnimate(QMacStylePrivate::Animates as, QWidget *w)
defaultButton = static_cast<QPushButton *>(w);
else if (as == AquaProgressBar)
progressBars.append(w);
else if (as == AquaScrollBar)
scrollBars.append(w);
startAnimationTimer();
}
void QMacStylePrivate::startAnimationTimer()
{
if ((defaultButton || !progressBars.isEmpty()) && timerID <= -1)
if ((defaultButton || !progressBars.isEmpty() || !scrollBars.isEmpty()) && timerID <= -1)
timerID = startTimer(animateSpeed(AquaListViewItemOpen));
}
@ -1674,7 +1746,8 @@ bool QMacStylePrivate::addWidget(QWidget *w)
{
//already knew of it
if (static_cast<QPushButton*>(w) == defaultButton
|| progressBars.contains(static_cast<QProgressBar*>(w)))
|| progressBars.contains(static_cast<QProgressBar*>(w))
|| scrollBars.contains(static_cast<QScrollBar*>(w)))
return false;
if (QPushButton *btn = qobject_cast<QPushButton *>(w)) {
@ -1689,6 +1762,12 @@ bool QMacStylePrivate::addWidget(QWidget *w)
startAnimate(AquaProgressBar, w);
return true;
}
bool isScrollBar = (qobject_cast<QScrollBar *>(w));
if (isScrollBar) {
w->installEventFilter(this);
startAnimate(AquaScrollBar, w);
return true;
}
}
if (w->isWindow()) {
w->installEventFilter(this);
@ -1704,6 +1783,8 @@ void QMacStylePrivate::removeWidget(QWidget *w)
stopAnimate(AquaPushButton, btn);
} else if (qobject_cast<QProgressBar *>(w)) {
stopAnimate(AquaProgressBar, w);
} else if (qobject_cast<QScrollBar *>(w)) {
stopAnimate(AquaScrollBar, w);
}
}
@ -1755,6 +1836,30 @@ void QMacStylePrivate::timerEvent(QTimerEvent *)
animated += i;
}
}
if (!scrollBars.isEmpty()) {
int i = 0;
while (i < scrollBars.size()) {
QWidget *maybeScroll = scrollBars.at(i);
if (!maybeScroll) {
scrollBars.removeAt(i);
} else {
if (QScrollBar *sb = qobject_cast<QScrollBar *>(maybeScroll)) {
const OverlayScrollBarInfo& info = scrollBarInfos[sb];
const QDateTime dt = QDateTime::currentDateTime();
const qreal elapsed = qMax(info.lastHovered.msecsTo(dt),
info.lastUpdate.msecsTo(dt));
const CGFloat opacity = 1.0 - qMax(0.0, (elapsed - ScrollBarFadeOutDelay) /
ScrollBarFadeOutDuration);
if ((opacity > 0.0 || !info.cleared) && (elapsed > ScrollBarFadeOutDelay)) {
if (doAnimate(AquaScrollBar))
sb->update();
}
}
++i;
++animated;
}
}
}
if (animated <= 0) {
killTimer(timerID);
timerID = -1;
@ -1776,6 +1881,63 @@ bool QMacStylePrivate::eventFilter(QObject *o, QEvent *e)
case QEvent::Hide:
progressBars.removeAll(pb);
}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
} else if (QScrollBar *sb = qobject_cast<QScrollBar *>(o)) {
// 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) {
OverlayScrollBarInfo& info = scrollBarInfos[sb];
const qreal elapsed = info.lastUpdate.msecsTo(QDateTime::currentDateTime());
const CGFloat opacity = 1.0 - qMax(0.0, (elapsed - ScrollBarFadeOutDelay)
/ 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 = QDateTime::currentDateTime();
info.lastHovered = QDateTime::currentDateTime();
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
} else if (QPushButton *btn = qobject_cast<QPushButton *>(o)) {
switch (e->type()) {
default:
@ -1943,10 +2105,29 @@ QMacStyle::QMacStyle()
: QWindowsStyle()
{
d = new QMacStylePrivate(this);
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
d->receiver = [[NotificationReceiver alloc] initWithPrivate:d];
NotificationReceiver *receiver = static_cast<NotificationReceiver *>(d->receiver);
[[NSNotificationCenter defaultCenter] addObserver:receiver
selector:@selector(scrollBarStyleDidChange:)
name:NSPreferredScrollerStyleDidChangeNotification
object:nil];
d->nsscroller = [[NSScroller alloc] init];
#endif
}
QMacStyle::~QMacStyle()
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
[d->nsscroller release];
NotificationReceiver *receiver = static_cast<NotificationReceiver *>(d->receiver);
[[NSNotificationCenter defaultCenter] removeObserver:receiver];
#endif
delete qt_mac_backgroundPattern;
qt_mac_backgroundPattern = 0;
delete d;
@ -2086,6 +2267,11 @@ void QMacStyle::polish(QWidget* w)
rubber->setAttribute(Qt::WA_PaintOnScreen, false);
rubber->setAttribute(Qt::WA_NoSystemBackground, false);
}
if (qobject_cast<QScrollBar*>(w)) {
w->setAttribute(Qt::WA_OpaquePaintEvent, false);
w->setMouseTracking(true);
}
}
void QMacStyle::unpolish(QWidget* w)
@ -2115,6 +2301,11 @@ void QMacStyle::unpolish(QWidget* w)
frame->setAttribute(Qt::WA_NoSystemBackground, true);
QWindowsStyle::unpolish(w);
if (qobject_cast<QScrollBar*>(w)) {
w->setAttribute(Qt::WA_OpaquePaintEvent, true);
w->setMouseTracking(false);
}
}
int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QWidget *widget) const
@ -2281,6 +2472,23 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW
}
break;
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))) {
switch (d->aquaSizeConstrain(opt, widget)) {
case QAquaSizeUnknown:
case QAquaSizeLarge:
ret = 9;
break;
case QAquaSizeMini:
case QAquaSizeSmall:
ret = 7;
break;
}
break;
}
#endif
switch (d->aquaSizeConstrain(opt, widget)) {
case QAquaSizeUnknown:
case QAquaSizeLarge:
@ -2472,6 +2680,15 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW
ret = 0;
}
break;
case PM_ScrollView_ScrollBarOverlap:
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
ret = (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7 &&
[NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay) ?
pixelMetric(PM_ScrollBarExtent, opt, widget) : 0;
#else
ret = 0;
#endif
break;
default:
ret = QWindowsStyle::pixelMetric(metric, opt, widget);
break;
@ -4868,38 +5085,175 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex
#endif
}
HIThemeDrawTrack(&tdi, tracking ? 0 : &macRect, cg,
kHIThemeOrientationNormal);
if (cc == CC_Slider && slider->subControls & SC_SliderTickmarks) {
if (qt_mac_is_metal(widget)) {
if (tdi.enableState == kThemeTrackInactive)
tdi.enableState = kThemeTrackActive; // Looks more Cocoa-like
#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)) &&
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 = QDateTime::currentDateTime();
showSiblings = true;
}
int interval = slider->tickInterval;
if (interval == 0) {
interval = slider->pageStep;
if (interval == 0)
interval = slider->singleStep;
if (interval == 0)
interval = 1;
}
int numMarks = 1 + ((slider->maximum - slider->minimum) / interval);
if (tdi.trackInfo.slider.thumbDir == kThemeThumbPlain) {
// They asked for both, so we'll give it to them.
tdi.trackInfo.slider.thumbDir = kThemeThumbDownward;
HIThemeDrawTrackTickMarks(&tdi, numMarks,
cg,
kHIThemeOrientationNormal);
tdi.trackInfo.slider.thumbDir = kThemeThumbUpward;
HIThemeDrawTrackTickMarks(&tdi, numMarks,
cg,
kHIThemeOrientationNormal);
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 = QDateTime::currentDateTime();
}
qreal elapsed = info.lastHovered.msecsTo(QDateTime::currentDateTime());
CGFloat opacity = 1.0 - qMax(0.0,
(elapsed - QMacStylePrivate::ScrollBarFadeOutDelay) /
QMacStylePrivate::ScrollBarFadeOutDuration);
const bool isHorizontal = slider->orientation == Qt::Horizontal;
if (info.hovered) {
info.lastHovered = QDateTime::currentDateTime();
info.lastUpdate = QDateTime::currentDateTime();
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;
}
}
// 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();
}
CGContextSaveGState(cg);
[NSGraphicsContext setCurrentContext:[NSGraphicsContext
graphicsContextWithGraphicsPort:(CGContextRef)cg flipped:NO]];
NSScroller *scroller = reinterpret_cast<NSScroller*>(d->nsscroller);
[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 bool isDarkBg = bgColor.red() < 128 && bgColor.green() < 128 &&
bgColor.blue() < 128;
if (isDarkBg)
[scroller setKnobStyle:NSScrollerKnobStyleLight];
[scroller setControlSize:(tdi.kind == kThemeSmallScrollBar ? NSMiniControlSize
: NSRegularControlSize)];
if (isHorizontal)
[scroller setBounds:NSMakeRect(0, -1,
slider->rect.width(), slider->rect.height())];
else
[scroller setBounds:NSMakeRect(-1, 0,
slider->rect.width(), slider->rect.height())];
[scroller setScrollerStyle:NSScrollerStyleOverlay];
// first we draw only the track, by using a disabled scroller
if (opacity > 0.0) {
CGContextBeginTransparencyLayerWithRect(cg, qt_hirectForQRect(slider->rect),
NULL);
CGContextSetAlpha(cg, opacity);
[scroller setFrame:NSMakeRect(0, 0, slider->rect.width(), slider->rect.height())];
[scroller setEnabled:NO];
[scroller displayRectIgnoringOpacity:[scroller bounds]
inContext:[NSGraphicsContext currentContext]];
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 = info.lastUpdate.msecsTo(QDateTime::currentDateTime());
opacity = 1.0 - qMax(0.0, (elapsed - QMacStylePrivate::ScrollBarFadeOutDelay) /
QMacStylePrivate::ScrollBarFadeOutDuration);
info.cleared = opacity <= 0.0;
CGContextBeginTransparencyLayerWithRect(cg, qt_hirectForQRect(slider->rect), NULL);
CGContextSetAlpha(cg, opacity);
const qreal length = slider->maximum - slider->minimum + slider->pageStep;
const qreal proportion = slider->pageStep / length;
qreal value = (slider->sliderValue - slider->minimum) / length;
if (isHorizontal && slider->direction == Qt::RightToLeft)
value = 1.0 - value - proportion;
[scroller setEnabled:(slider->state & State_Enabled) ? YES : NO];
[scroller setKnobProportion:1.0];
const int minKnobWidth = 26;
if (isHorizontal) {
const qreal plannedWidth = proportion * slider->rect.width();
const qreal width = qMax<qreal>(minKnobWidth, plannedWidth);
const qreal totalWidth = slider->rect.width() + plannedWidth - width;
[scroller setFrame:NSMakeRect(0, 0, width, slider->rect.height())];
CGContextTranslateCTM(cg, value * totalWidth, 0);
} else {
HIThemeDrawTrackTickMarks(&tdi, numMarks,
cg,
kHIThemeOrientationNormal);
const qreal plannedHeight = proportion * slider->rect.height();
const qreal height = qMax<qreal>(minKnobWidth, plannedHeight);
const qreal totalHeight = slider->rect.height() + plannedHeight - height;
[scroller setFrame:NSMakeRect(0, 0, slider->rect.width(), height)];
CGContextTranslateCTM(cg, 0, value * totalHeight);
}
if (value > 0.0) {
[scroller layout];
[scroller displayRectIgnoringOpacity:[scroller bounds]
inContext:[NSGraphicsContext currentContext]];
}
CGContextEndTransparencyLayer(cg);
CGContextRestoreGState(cg);
} else
#endif
{
HIThemeDrawTrack(&tdi, tracking ? 0 : &macRect, cg,
kHIThemeOrientationNormal);
if (cc == CC_Slider && slider->subControls & SC_SliderTickmarks) {
if (qt_mac_is_metal(widget)) {
if (tdi.enableState == kThemeTrackInactive)
tdi.enableState = kThemeTrackActive; // Looks more Cocoa-like
}
int interval = slider->tickInterval;
if (interval == 0) {
interval = slider->pageStep;
if (interval == 0)
interval = slider->singleStep;
if (interval == 0)
interval = 1;
}
int numMarks = 1 + ((slider->maximum - slider->minimum) / interval);
if (tdi.trackInfo.slider.thumbDir == kThemeThumbPlain) {
// They asked for both, so we'll give it to them.
tdi.trackInfo.slider.thumbDir = kThemeThumbDownward;
HIThemeDrawTrackTickMarks(&tdi, numMarks,
cg,
kHIThemeOrientationNormal);
tdi.trackInfo.slider.thumbDir = kThemeThumbUpward;
HIThemeDrawTrackTickMarks(&tdi, numMarks,
cg,
kHIThemeOrientationNormal);
} else {
HIThemeDrawTrackTickMarks(&tdi, numMarks,
cg,
kHIThemeOrientationNormal);
}
}
}
}

View File

@ -155,13 +155,14 @@ public:
static const int BevelButtonW;
static const int BevelButtonH;
static const int PushButtonContentPadding;
static const qreal ScrollBarFadeOutDuration;
static const qreal ScrollBarFadeOutDelay;
// Stuff from QAquaAnimate:
bool addWidget(QWidget *);
void removeWidget(QWidget *);
enum Animates { AquaPushButton, AquaProgressBar, AquaListViewItemOpen };
enum Animates { AquaPushButton, AquaProgressBar, AquaListViewItemOpen, AquaScrollBar };
bool animatable(Animates, const QWidget *) const;
void stopAnimate(Animates, QWidget *);
void startAnimate(Animates, QWidget *);
@ -210,6 +211,28 @@ public:
QPointer<QPushButton> defaultButton; //default push buttons
int timerID;
QList<QPointer<QWidget> > progressBars; //existing progress bars that need animation
QList<QPointer<QWidget> > scrollBars; //existing scroll bars that need animation
struct OverlayScrollBarInfo {
OverlayScrollBarInfo()
: lastValue(-1),
lastMinimum(-1),
lastMaximum(-1),
lastUpdate(QDateTime::currentDateTime()),
hovered(false),
lastHovered(QDateTime::fromTime_t(0)),
cleared(false)
{}
int lastValue;
int lastMinimum;
int lastMaximum;
QSize lastSize;
QDateTime lastUpdate;
bool hovered;
QDateTime lastHovered;
bool cleared;
};
QMap<const QWidget*, OverlayScrollBarInfo> scrollBarInfos;
struct ButtonState {
int frame;
@ -220,6 +243,10 @@ public:
CFAbsoluteTime defaultButtonStart;
QMacStyle *q;
bool mouseDown;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
void* receiver;
void *nsscroller;
#endif
};
QT_END_NAMESPACE

View File

@ -1497,6 +1497,8 @@ void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment,
\value PM_ScrollView_ScrollBarSpacing Distance between frame and scrollbar
with SH_ScrollView_FrameOnlyAroundContents set.
\value PM_ScrollView_ScrollBarOverlap Overlap between scroll bars and scroll content
\value PM_SubMenuOverlap The horizontal overlap between a submenu and its parent.

View File

@ -553,6 +553,7 @@ public:
PM_TabCloseIndicatorHeight,
PM_ScrollView_ScrollBarSpacing,
PM_ScrollView_ScrollBarOverlap,
PM_SubMenuOverlap,
// do not add any values below/greater than this

View File

@ -52,6 +52,7 @@
#include "qboxlayout.h"
#include "qpainter.h"
#include "qmargins.h"
#include "qheaderview.h"
#include <QDebug>
@ -327,6 +328,11 @@ void QAbstractScrollAreaPrivate::layoutChildren()
bool needv = (vbarpolicy == Qt::ScrollBarAlwaysOn
|| (vbarpolicy == Qt::ScrollBarAsNeeded && vbar->minimum() < vbar->maximum()));
QStyleOption opt(0);
opt.init(q);
const int scrollOverlap = q->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarOverlap,
&opt, q);
#ifdef Q_WS_MAC
QWidget * const window = q->window();
@ -365,8 +371,6 @@ void QAbstractScrollAreaPrivate::layoutChildren()
const QSize extSize(vsbExt, hsbExt);
const QRect widgetRect = q->rect();
QStyleOption opt(0);
opt.init(q);
const bool hasCornerWidget = (cornerWidget != 0);
@ -394,7 +398,7 @@ void QAbstractScrollAreaPrivate::layoutChildren()
}
#endif
QPoint cornerOffset(needv ? vsbExt : 0, needh ? hsbExt : 0);
QPoint cornerOffset((needv && scrollOverlap == 0) ? vsbExt : 0, (needh && scrollOverlap == 0) ? hsbExt : 0);
QRect controlsRect;
QRect viewportRect;
@ -403,7 +407,7 @@ void QAbstractScrollAreaPrivate::layoutChildren()
if ((frameStyle != QFrame::NoFrame) &&
q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &opt, q)) {
controlsRect = widgetRect;
const int extra = q->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing, &opt, q);
const int extra = scrollOverlap;
const QPoint cornerExtra(needv ? extra : 0, needh ? extra : 0);
QRect frameRect = widgetRect;
frameRect.adjust(0, 0, -cornerOffset.x() - cornerExtra.x(), -cornerOffset.y() - cornerExtra.y());
@ -418,9 +422,11 @@ void QAbstractScrollAreaPrivate::layoutChildren()
viewportRect = QRect(controlsRect.topLeft(), controlsRect.bottomRight() - cornerOffset);
}
cornerOffset = QPoint(needv ? vsbExt : 0, needh ? hsbExt : 0);
// If we have a corner widget and are only showing one scroll bar, we need to move it
// to make room for the corner widget.
if (hasCornerWidget && (needv || needh))
if (hasCornerWidget && (needv || needh) && scrollOverlap == 0)
cornerOffset = extPoint;
#ifdef Q_WS_MAC
@ -436,7 +442,7 @@ void QAbstractScrollAreaPrivate::layoutChildren()
// Some styles paints the corner if both scorllbars are showing and there is
// no corner widget. Also, on the Mac we paint if there is a native
// (transparent) sizegrip in the area where a corner widget would be.
if ((needv && needh && hasCornerWidget == false)
if ((needv && needh && hasCornerWidget == false && scrollOverlap == 0)
|| ((needv || needh)
#ifdef Q_WS_MAC
&& hasMacSizeGrip
@ -455,8 +461,24 @@ void QAbstractScrollAreaPrivate::layoutChildren()
reverseCornerPaintingRect = QRect();
#endif
// move the scrollbars away from top/left headers
int vHeaderRight = 0;
int hHeaderBottom = 0;
if (scrollOverlap > 0 && (needv || needh)) {
const QList<QHeaderView *> headers = q->findChildren<QHeaderView*>();
if (headers.count() <= 2) {
Q_FOREACH (const QHeaderView *header, headers) {
const QRect geo = header->geometry();
if (header->orientation() == Qt::Vertical && header->isVisible() && QStyle::visualRect(opt.direction, opt.rect, geo).left() <= opt.rect.width() / 2)
vHeaderRight = QStyle::visualRect(opt.direction, opt.rect, geo).right();
else if (header->orientation() == Qt::Horizontal && header->isVisible() && geo.top() <= q->frameWidth())
hHeaderBottom = geo.bottom();
}
}
}
if (needh) {
QRect horizontalScrollBarRect(QPoint(controlsRect.left(), cornerPoint.y()), QPoint(cornerPoint.x() - 1, controlsRect.bottom()));
QRect horizontalScrollBarRect(QPoint(controlsRect.left() + vHeaderRight, cornerPoint.y()), QPoint(cornerPoint.x() - 1, controlsRect.bottom()));
#ifdef Q_WS_MAC
if (hasMacReverseSizeGrip)
horizontalScrollBarRect.adjust(vsbExt, 0, 0, 0);
@ -466,7 +488,7 @@ void QAbstractScrollAreaPrivate::layoutChildren()
}
if (needv) {
const QRect verticalScrollBarRect (QPoint(cornerPoint.x(), controlsRect.top()), QPoint(controlsRect.right(), cornerPoint.y() - 1));
const QRect verticalScrollBarRect (QPoint(cornerPoint.x(), controlsRect.top() + hHeaderBottom), QPoint(controlsRect.right(), cornerPoint.y() - 1));
scrollBarContainers[Qt::Vertical]->setGeometry(QStyle::visualRect(opt.direction, opt.rect, verticalScrollBarRect));
scrollBarContainers[Qt::Vertical]->raise();
}